diff --git a/src/libs/utils/elfreader.cpp b/src/libs/utils/elfreader.cpp new file mode 100644 index 00000000000..ade7311a441 --- /dev/null +++ b/src/libs/utils/elfreader.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "elfreader.h" +#include "qtcassert.h" + +#include +#include +#include + +namespace Utils { + +template +T read(const char *s, ElfReader::ElfEndian endian) +{ + if (endian == ElfReader::ElfBigEndian) + return qFromBigEndian(reinterpret_cast(s)); + else + return qFromLittleEndian(reinterpret_cast(s)); +} + +ElfReader::ElfReader(const QString &binary) + : m_binary(binary) +{ +} + +const char *ElfReader::parseSectionHeader(const char *data, ElfSectionHeader *sh) +{ + sh->name = read(data, m_endian); + data += sizeof(qelfword_t); // sh_name + sh->type = read(data, m_endian); + data += sizeof(qelfword_t) // sh_type + + sizeof(qelfaddr_t) // sh_flags + + sizeof(qelfaddr_t); // sh_addr + sh->offset = read(data, m_endian); + data += sizeof(qelfoff_t); // sh_offset + sh->size = read(data, m_endian); + data += sizeof(qelfoff_t); // sh_size + return data; +} + +ElfReader::Result ElfReader::parse(const char *dataStart, quint64 fdlen, + QList *sectionNames) +{ + if (fdlen < 64) { + m_errorString = QLibrary::tr("'%1' is not an ELF object (%2)") + .arg(m_binary).arg(QLatin1String("file too small")); + return NotElf; + } + const char *data = dataStart; + if (strncmp(data, "\177ELF", 4) != 0) { + m_errorString = QLibrary::tr("'%1' is not an ELF object") + .arg(m_binary); + return NotElf; + } + + // 32 or 64 bit + if (data[4] != 1 && data[4] != 2) { + m_errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") + .arg(m_binary).arg(QLatin1String("odd cpu architecture")); + return Corrupt; + } + + // int 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 && bits != 32) + // || (sizeof(void*) == 8 && bits != 64)) { + // if (errorString) + // *errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") + // .arg(m_binary).arg(QLatin1String("wrong cpu architecture")); + // return Corrupt; + // } + + // Read Endianess. + if (data[5] == 0) { + m_errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") + .arg(m_binary).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(data, m_endian); + data += sizeof(qelfoff_t) // e_shoff + + sizeof(qelfword_t); // e_flags + + qelfhalf_t e_shsize = read(data, m_endian); + + if (e_shsize > fdlen) { + m_errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") + .arg(m_binary).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(data, m_endian); + + if (e_shentsize % 4) { + m_errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") + .arg(m_binary).arg(QLatin1String("unexpected e_shentsize")); + return Corrupt; + } + data += sizeof(qelfhalf_t); // e_shentsize + qelfhalf_t e_shnum = read(data, m_endian); + data += sizeof(qelfhalf_t); // e_shnum + qelfhalf_t e_shtrndx = read(data, m_endian); + data += sizeof(qelfhalf_t); // e_shtrndx + + if (quint32(e_shnum * e_shentsize) > fdlen) { + m_errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") + .arg(m_binary) + .arg(QLatin1String("announced %2 sections, each %3 bytes, exceed file size")) + .arg(e_shnum).arg(e_shentsize); + return Corrupt; + } + + ElfSectionHeader strtab; + qulonglong soff = e_shoff + e_shentsize * (e_shtrndx); + + if ((soff + e_shentsize) > fdlen || soff % 4 || soff == 0) { + m_errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") + .arg(m_binary) + .arg(QLatin1String("shstrtab section header seems to be at %1")) + .arg(QString::number(soff, 16)); + return Corrupt; + } + + parseSectionHeader(dataStart + soff, &strtab); + const int stringTableFileOffset = strtab.offset; + + if (quint32(stringTableFileOffset + e_shentsize) >= fdlen + || stringTableFileOffset == 0) { + m_errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") + .arg(m_binary) + .arg(QLatin1String("string table seems to be at %1")) + .arg(QString::number(soff, 16)); + return Corrupt; + } + + 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 + stringTableFileOffset + sh.name; + + if (stringTableFileOffset + sh.name > fdlen) { + m_errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") + .arg(m_binary) + .arg(QLatin1String("section name %2 of %3 behind end of file")) + .arg(i).arg(e_shnum); + return Corrupt; + } + + if (sectionNames) + sectionNames->append(shnam); + + s += e_shentsize; + } + return Ok; +} + +QList ElfReader::sectionNames() +{ + QList names; + + QFile file(m_binary); + if (!file.open(QIODevice::ReadOnly)) { + m_errorString = file.errorString(); + return names; + } + + QByteArray data; + const char *filedata = 0; + quint64 fdlen = file.size(); + filedata = (char *) file.map(0, fdlen); + if (filedata == 0) { + // Try reading the data into memory instead. + data = file.readAll(); + filedata = data.constData(); + fdlen = data.size(); + } + + parse(filedata, fdlen, &names); + return names; +} + +} // namespace Utils diff --git a/src/libs/utils/elfreader.h b/src/libs/utils/elfreader.h new file mode 100644 index 00000000000..97449508f0e --- /dev/null +++ b/src/libs/utils/elfreader.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This is essentially copied from Qt 5's +// qtbase/src/corelib/plugin/qelfparser_p.{h,cpp} + +#ifndef UTILS_ELFREADER_H +#define UTILS_ELFREADER_H + +#include "utils_global.h" + +#include +#include + +namespace Utils { + +typedef quint16 qelfhalf_t; +typedef quint32 qelfword_t; +typedef quintptr qelfoff_t; +typedef quintptr qelfaddr_t; + +class QTCREATOR_UTILS_EXPORT ElfReader +{ +public: + explicit ElfReader(const QString &binary); + + struct ElfSectionHeader + { + qelfword_t name; + qelfword_t type; + qelfoff_t offset; + qelfoff_t size; + }; + + enum ElfEndian { ElfLittleEndian = 0, ElfBigEndian = 1 }; + QList sectionNames(); + QString errorString() const { return m_errorString; } + +private: + enum Result { Ok, NotElf, Corrupt }; + + const char *parseSectionHeader(const char *s, ElfSectionHeader *sh); + Result parse(const char *dataStart, quint64 fdlen, QList *sectionNames); + + QString m_binary; + QString m_errorString; + ElfEndian m_endian; +}; + +} // namespace Utils + +#endif // UTILS_ELFREADER_H diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri index 807f72cf5f2..86bf5cf4692 100644 --- a/src/libs/utils/utils-lib.pri +++ b/src/libs/utils/utils-lib.pri @@ -77,7 +77,8 @@ SOURCES += $$PWD/environment.cpp \ $$PWD/tcpportsgatherer.cpp \ $$PWD/appmainwindow.cpp \ $$PWD/basetreeview.cpp \ - $$PWD/qtcassert.cpp + $$PWD/qtcassert.cpp \ + $$PWD/elfreader.cpp win32 { SOURCES += \ @@ -161,7 +162,8 @@ HEADERS += \ $$PWD/portlist.h \ $$PWD/tcpportsgatherer.h \ $$PWD/appmainwindow.h \ - $$PWD/basetreeview.h + $$PWD/basetreeview.h \ + $$PWD/elfreader.h FORMS += $$PWD/filewizardpage.ui \ $$PWD/projectintropage.ui \ diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 13482868e3e..eec5d9ee116 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -51,6 +51,8 @@ QtcLibrary { "detailsbutton.h", "detailswidget.cpp", "detailswidget.h", + "elfreader.cpp", + "elfreader.h", "environment.h", "environmentmodel.cpp", "environmentmodel.h",