diff options
Diffstat (limited to 'src/corelib')
-rw-r--r-- | src/corelib/global/qglobal.h | 6 | ||||
-rw-r--r-- | src/corelib/plugin/plugin.pri | 6 | ||||
-rw-r--r-- | src/corelib/plugin/qelfparser_p.cpp | 234 | ||||
-rw-r--r-- | src/corelib/plugin/qelfparser_p.h | 103 | ||||
-rw-r--r-- | src/corelib/plugin/qlibrary.cpp | 76 | ||||
-rw-r--r-- | src/corelib/plugin/qplugin.h | 13 |
6 files changed, 388 insertions, 50 deletions
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index a8758cf..ede6dcd 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -2612,6 +2612,12 @@ QT_LICENSED_MODULE(DBus) # define QT_NO_PROCESS #endif +#if defined (__ELF__) +# if defined (Q_OS_LINUX) || defined (Q_OS_SOLARIS) || defined (Q_OS_FREEBSD) || defined (Q_OS_OPENBSD) || defined (Q_OS_IRIX) +# define Q_OF_ELF +# endif +#endif + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/plugin/plugin.pri b/src/corelib/plugin/plugin.pri index c05ff48..2e7db5d 100644 --- a/src/corelib/plugin/plugin.pri +++ b/src/corelib/plugin/plugin.pri @@ -7,13 +7,15 @@ HEADERS += \ plugin/qlibrary_p.h \ plugin/qplugin.h \ plugin/quuid.h \ - plugin/qfactoryloader_p.h + plugin/qfactoryloader_p.h \ + plugin/qelfparser_p.h SOURCES += \ plugin/qpluginloader.cpp \ plugin/qfactoryloader.cpp \ plugin/quuid.cpp \ - plugin/qlibrary.cpp + plugin/qlibrary.cpp \ + plugin/qelfparser_p.cpp win32 { SOURCES += plugin/qlibrary_win.cpp diff --git a/src/corelib/plugin/qelfparser_p.cpp b/src/corelib/plugin/qelfparser_p.cpp new file mode 100644 index 0000000..97008fd --- /dev/null +++ b/src/corelib/plugin/qelfparser_p.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlibrary_p.h" +#include "qelfparser_p.h" +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +// #define QELFPARSER_DEBUG 1 + +const char *QElfParser::parseSectionHeader(const char *data, ElfSectionHeader *sh) +{ + sh->name = read<qelfword_t>(data); + data += sizeof(qelfword_t); // sh_name + sh->type = read<qelfword_t>(data); + data += sizeof(qelfword_t) // sh_type + + sizeof(qelfaddr_t) // sh_flags + + sizeof(qelfaddr_t); // sh_addr + sh->offset = read<qelfoff_t>(data); + data += sizeof(qelfoff_t); // sh_offset + sh->size = read<qelfword_t>(data); + data += sizeof(qelfword_t); // sh_size + return data; +} + +int QElfParser::parse(const char *dataStart, ulong fdlen, const QString &library, QLibraryPrivate *lib, long *pos, ulong *sectionlen) +{ +#if defined(QELFPARSER_DEBUG) + qDebug() << "QElfParser::parse " << library; +#endif + + if (fdlen < 64){ + if (lib) + lib->errorString = QLibrary::tr("'%1' is not an ELF object (%2)").arg(library).arg(QLatin1String("file too small")); + return NotElf; + } + const char *data = dataStart; + if (qstrncmp(data, "\177ELF", 4) != 0) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is not an ELF object").arg(library); + return NotElf; + } + // 32 or 64 bit + if (data[4] != 1 && data[4] != 2) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("odd cpu architecture")); + return Corrupt; + } + m_bits = (data[4] << 5); + + /* If you remove this check, to read ELF objects of a different arch, please make sure you modify the typedefs + to match the _plugin_ architecture. + */ + if ((sizeof(void*) == 4 && m_bits != 32) || (sizeof(void*) == 8 && m_bits != 64)) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("wrong cpu architecture")); + return Corrupt; + } + // endian + if (data[5] == 0) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("odd endianess")); + return Corrupt; + } + m_endian = (data[5] == 1 ? ElfLittleEndian : ElfBigEndian); + + data += 16 // e_ident + + sizeof(qelfhalf_t) // e_type + + sizeof(qelfhalf_t) // e_machine + + sizeof(qelfword_t) // e_version + + sizeof(qelfaddr_t) // e_entry + + sizeof(qelfoff_t); // e_phoff + + qelfoff_t e_shoff = read<qelfoff_t> (data); + data += sizeof(qelfoff_t) // e_shoff + + sizeof(qelfword_t); // e_flags + + qelfhalf_t e_shsize = read<qelfhalf_t> (data); + + if (e_shsize > fdlen) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("unexpected e_shsize")); + return Corrupt; + } + + data += sizeof(qelfhalf_t) // e_ehsize + + sizeof(qelfhalf_t) // e_phentsize + + sizeof(qelfhalf_t); // e_phnum + + qelfhalf_t e_shentsize = read<qelfhalf_t> (data); + + if (e_shentsize % 8){ + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("unexpected e_shentsize")); + return Corrupt; + } + data += sizeof(qelfhalf_t); // e_shentsize + qelfhalf_t e_shnum = read<qelfhalf_t> (data); + data += sizeof(qelfhalf_t); // e_shnum + qelfhalf_t e_shtrndx = read<qelfhalf_t> (data); + data += sizeof(qelfhalf_t); // e_shtrndx + + if ((e_shnum * e_shentsize) > fdlen) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("announced %2 sections, each %3 bytes, exceed file size")) + .arg(e_shnum).arg(e_shentsize); + return Corrupt; + } + +#if defined(QELFPARSER_DEBUG) + qDebug() << e_shnum << "sections starting at " << ("0x" + QByteArray::number(e_shoff, 16)).data() << "each" << e_shentsize << "bytes"; +#endif + + ElfSectionHeader strtab; + qulonglong soff = e_shoff + e_shentsize * (e_shtrndx); + + if ((soff + e_shentsize) > fdlen || soff % 8 || soff == 0) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("strtab section seems to be at %1")) + .arg(QString::number(soff, 16)); + return Corrupt; + } + + parseSectionHeader(dataStart + soff, &strtab); + m_stringTableFileOffset = strtab.offset; + + if ((m_stringTableFileOffset + e_shentsize) >= fdlen || m_stringTableFileOffset == 0) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("string table seems to be at %1")) + .arg(QString::number(soff, 16)); + return Corrupt; + } + +#if defined(QELFPARSER_DEBUG) + qDebug(".shstrtab at 0x%s", QByteArray::number(m_stringTableFileOffset, 16).data()); +#endif + + const char *s = dataStart + e_shoff; + for (int i = 0; i < e_shnum; ++i) { + ElfSectionHeader sh; + parseSectionHeader(s, &sh); + if (sh.name == 0) { + s += e_shentsize; + continue; + } + const char *shnam = dataStart + m_stringTableFileOffset + sh.name; + + if (m_stringTableFileOffset + sh.name > fdlen) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("section name %2 of %3 behind end of file")) + .arg(i).arg(e_shnum); + return Corrupt; + } + +#if defined(QELFPARSER_DEBUG) + qDebug() << "++++" << i << shnam; +#endif + + if (qstrcmp(shnam, ".qtplugin") == 0 || qstrcmp(shnam, ".rodata") == 0) { + if (!(sh.type & 0x1)) { + if (shnam[1] == 'r') { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("empty .rodata. not a library.")); + return Corrupt; + } +#if defined(QELFPARSER_DEBUG) + qDebug()<<"section is not program data. skipped."; +#endif + s += e_shentsize; + continue; + } + + if (sh.offset == 0 || (sh.offset + sh.size) > fdlen) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("missing section data. This is not a library.")); + return Corrupt; + } + *pos = sh.offset; + *sectionlen = sh.size - 1; + if (shnam[1] == 'q') + return Ok; + } + s += e_shentsize; + } + return NoQtSection; +} + +QT_END_NAMESPACE + diff --git a/src/corelib/plugin/qelfparser_p.h b/src/corelib/plugin/qelfparser_p.h new file mode 100644 index 0000000..380d5a1 --- /dev/null +++ b/src/corelib/plugin/qelfparser_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QELFPARSER_P_H +#define QELFPARSER_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> + +QT_BEGIN_NAMESPACE + +class QString; +class QLibraryPrivate; + +typedef quint16 qelfhalf_t; +typedef quint32 qelfword_t; +typedef quintptr qelfoff_t; +typedef quintptr qelfaddr_t; + +class QElfParser +{ +public: + enum {Ok = 0, NotElf = 1, NoQtSection = 2, Corrupt = 3}; + enum {ElfLittleEndian = 0, ElfBigEndian = 1}; + + struct ElfSectionHeader + { + qelfword_t name; + qelfword_t type; + qelfoff_t offset; + qelfword_t size; + }; + + int m_endian; + int m_bits; + int m_stringTableFileOffset; + + template <typename T> + T read(const char *s) + { + if (m_endian == ElfBigEndian) + return qFromBigEndian<T>(reinterpret_cast<const uchar *>(s)); + else + return qFromLittleEndian<T>(reinterpret_cast<const uchar *>(s)); + } + + const char *parseSectionHeader(const char* s, ElfSectionHeader *sh); + int parse(const char *m_s, ulong fdlen, const QString &library, QLibraryPrivate *lib, long *pos, ulong *sectionlen); +}; + +QT_END_NAMESPACE + +#endif // QELFPARSER_P_H + diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index f6ae038..eb464c7 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -38,7 +38,6 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - #include "qplatformdefs.h" #include "qlibrary.h" @@ -61,6 +60,7 @@ #include <qdebug.h> #include <qvector.h> #include <qdir.h> +#include "qelfparser_p.h" QT_BEGIN_NAMESPACE @@ -295,14 +295,6 @@ static bool qt_parse_pattern(const char *s, uint *version, bool *debug, QByteArr #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_SYMBIAN) && !defined(QT_NO_PLUGIN_CHECK) -#if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX) -# define USE_MMAP -QT_BEGIN_INCLUDE_NAMESPACE -# include <sys/types.h> -# include <sys/mman.h> -QT_END_INCLUDE_NAMESPACE -#endif // Q_OS_FREEBSD || Q_OS_LINUX - static long qt_find_pattern(const char *s, ulong s_len, const char *pattern, ulong p_len) { @@ -363,57 +355,47 @@ static bool qt_unix_query(const QString &library, uint *version, bool *debug, QB } QByteArray data; - char *filedata = 0; - ulong fdlen = 0; - -# ifdef USE_MMAP - char *mapaddr = 0; - size_t maplen = file.size(); - mapaddr = (char *) mmap(mapaddr, maplen, PROT_READ, MAP_PRIVATE, file.handle(), 0); - if (mapaddr != MAP_FAILED) { - // mmap succeeded - filedata = mapaddr; - fdlen = maplen; - } else { - // mmap failed - if (qt_debug_component()) { - qWarning("mmap: %s", qPrintable(qt_error_string(errno))); - } - if (lib) - lib->errorString = QLibrary::tr("Could not mmap '%1': %2") - .arg(library) - .arg(qt_error_string()); -# endif // USE_MMAP + const char *filedata = 0; + ulong fdlen = file.size(); + filedata = (char *) file.map(0, fdlen); + if (filedata == 0) { // try reading the data into memory instead data = file.readAll(); - filedata = data.data(); + filedata = data.constData(); fdlen = data.size(); -# ifdef USE_MMAP } -# endif // USE_MMAP - // verify that the pattern is present in the plugin + /* + ELF binaries on GNU, have .qplugin sections. + */ + long pos = 0; const char pattern[] = "pattern=QT_PLUGIN_VERIFICATION_DATA"; const ulong plen = qstrlen(pattern); - long pos = qt_find_pattern(filedata, fdlen, pattern, plen); - +#if defined (Q_OF_ELF) && defined(Q_CC_GNU) + int r = QElfParser().parse(filedata, fdlen, library, lib, &pos, &fdlen); + if (r == QElfParser::NoQtSection) { + if (pos > 0) { + // find inside .rodata + long rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen); + if (rel < 0) { + pos = -1; + } else { + pos += rel; + } + } else { + pos = qt_find_pattern(filedata, fdlen, pattern, plen); + } + } else if (r != QElfParser::Ok) + return false; +#else + pos = qt_find_pattern(filedata, fdlen, pattern, plen); +#endif // defined(Q_OF_ELF) && defined(Q_CC_GNU) bool ret = false; if (pos >= 0) ret = qt_parse_pattern(filedata + pos, version, debug, key); if (!ret && lib) lib->errorString = QLibrary::tr("Plugin verification data mismatch in '%1'").arg(library); -# ifdef USE_MMAP - if (mapaddr != MAP_FAILED && munmap(mapaddr, maplen) != 0) { - if (qt_debug_component()) - qWarning("munmap: %s", qPrintable(qt_error_string(errno))); - if (lib) - lib->errorString = QLibrary::tr("Could not unmap '%1': %2") - .arg(library) - .arg( qt_error_string() ); - } -# endif // USE_MMAP - file.close(); return ret; } diff --git a/src/corelib/plugin/qplugin.h b/src/corelib/plugin/qplugin.h index 7f541f1..bd49b15 100644 --- a/src/corelib/plugin/qplugin.h +++ b/src/corelib/plugin/qplugin.h @@ -101,13 +101,17 @@ void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunctio // NOTE: if you change pattern, you MUST change the pattern in // qlibrary.cpp as well. changing the pattern will break all // backwards compatibility as well (no old plugins will be loaded). +// QT5: should probably remove the entire pattern thing and do the section +// trick for all platforms. for now, keep it and fallback to scan for it. # ifdef QPLUGIN_DEBUG_STR # undef QPLUGIN_DEBUG_STR # endif # ifdef QT_NO_DEBUG # define QPLUGIN_DEBUG_STR "false" +# define QPLUGIN_SECTION_DEBUG_STR "" # else # define QPLUGIN_DEBUG_STR "true" +# define QPLUGIN_SECTION_DEBUG_STR ".debug" # endif # define Q_PLUGIN_VERIFICATION_DATA \ static const char qt_plugin_verification_data[] = \ @@ -116,6 +120,13 @@ void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunctio "debug="QPLUGIN_DEBUG_STR"\n" \ "buildkey="QT_BUILD_KEY; +# if defined (Q_OF_ELF) && defined (Q_CC_GNU) +# define Q_PLUGIN_VERIFICATION_SECTION \ + __attribute__ ((section (".qtplugin"))) __attribute__((used)) +# else +# define Q_PLUGIN_VERIFICATION_SECTION +# endif + # if defined (Q_OS_WIN32) && defined(Q_CC_BOR) # define Q_STANDARD_CALL __stdcall # else @@ -123,7 +134,7 @@ void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunctio # endif # define Q_EXPORT_PLUGIN2(PLUGIN, PLUGINCLASS) \ - Q_PLUGIN_VERIFICATION_DATA \ + Q_PLUGIN_VERIFICATION_SECTION Q_PLUGIN_VERIFICATION_DATA \ Q_EXTERN_C Q_DECL_EXPORT \ const char * Q_STANDARD_CALL qt_plugin_query_verification_data() \ { return qt_plugin_verification_data; } \ |