summaryrefslogtreecommitdiffstats
path: root/src/tools/windeployqt/elfreader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/windeployqt/elfreader.cpp')
-rw-r--r--src/tools/windeployqt/elfreader.cpp440
1 files changed, 440 insertions, 0 deletions
diff --git a/src/tools/windeployqt/elfreader.cpp b/src/tools/windeployqt/elfreader.cpp
new file mode 100644
index 0000000000..f375f5841d
--- /dev/null
+++ b/src/tools/windeployqt/elfreader.cpp
@@ -0,0 +1,440 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "elfreader.h"
+
+#include <QDir>
+
+QT_BEGIN_NAMESPACE
+
+/* This is a copy of the ELF reader contained in Qt Creator (src/libs/utils),
+ * extended by the dependencies() function to read out the dependencies of a dynamic executable. */
+
+quint16 getHalfWord(const unsigned char *&s, const ElfData &context)
+{
+ quint16 res;
+ if (context.endian == Elf_ELFDATA2MSB)
+ res = qFromBigEndian<quint16>(s);
+ else
+ res = qFromLittleEndian<quint16>(s);
+ s += 2;
+ return res;
+}
+
+quint32 getWord(const unsigned char *&s, const ElfData &context)
+{
+ quint32 res;
+ if (context.endian == Elf_ELFDATA2MSB)
+ res = qFromBigEndian<quint32>(s);
+ else
+ res = qFromLittleEndian<quint32>(s);
+ s += 4;
+ return res;
+}
+
+quint64 getAddress(const unsigned char *&s, const ElfData &context)
+{
+ quint64 res;
+ if (context.elfclass == Elf_ELFCLASS32) {
+ if (context.endian == Elf_ELFDATA2MSB)
+ res = qFromBigEndian<quint32>(s);
+ else
+ res = qFromLittleEndian<quint32>(s);
+ s += 4;
+ } else {
+ if (context.endian == Elf_ELFDATA2MSB)
+ res = qFromBigEndian<quint64>(s);
+ else
+ res = qFromLittleEndian<quint64>(s);
+ s += 8;
+ }
+ return res;
+}
+
+quint64 getOffset(const unsigned char *&s, const ElfData &context)
+{
+ return getAddress(s, context);
+}
+
+static void parseSectionHeader(const uchar *s, ElfSectionHeader *sh, const ElfData &context)
+{
+ sh->index = getWord(s, context);
+ sh->type = getWord(s, context);
+ sh->flags = quint32(getOffset(s, context));
+ sh->addr = getAddress(s, context);
+ sh->offset = getOffset(s, context);
+ sh->size = getOffset(s, context);
+}
+
+static void parseProgramHeader(const uchar *s, ElfProgramHeader *sh, const ElfData &context)
+{
+ sh->type = getWord(s, context);
+ sh->offset = getOffset(s, context);
+ /* p_vaddr = */ getAddress(s, context);
+ /* p_paddr = */ getAddress(s, context);
+ sh->filesz = getWord(s, context);
+ sh->memsz = getWord(s, context);
+}
+
+class ElfMapper
+{
+public:
+ ElfMapper(const ElfReader *reader) : file(reader->m_binary) {}
+
+ bool map()
+ {
+ if (!file.open(QIODevice::ReadOnly))
+ return false;
+
+ fdlen = quint64(file.size());
+ ustart = file.map(0, qint64(fdlen));
+ if (ustart == 0) {
+ // Try reading the data into memory instead.
+ raw = file.readAll();
+ start = raw.constData();
+ fdlen = quint64(raw.size());
+ }
+ return true;
+ }
+
+public:
+ QFile file;
+ QByteArray raw;
+ union { const char *start; const uchar *ustart; };
+ quint64 fdlen;
+};
+
+ElfReader::ElfReader(const QString &binary)
+ : m_binary(binary)
+{
+}
+
+ElfData ElfReader::readHeaders()
+{
+ readIt();
+ return m_elfData;
+}
+
+static inline QString msgInvalidElfObject(const QString &binary, const QString &why)
+{
+ return QStringLiteral("'%1' is an invalid ELF object (%2)")
+ .arg(QDir::toNativeSeparators(binary), why);
+}
+
+ElfReader::Result ElfReader::readIt()
+{
+ if (!m_elfData.sectionHeaders.isEmpty())
+ return Ok;
+ if (!m_elfData.programHeaders.isEmpty())
+ return Ok;
+
+ ElfMapper mapper(this);
+ if (!mapper.map())
+ return Corrupt;
+
+ const quint64 fdlen = mapper.fdlen;
+
+ if (fdlen < 64) {
+ m_errorString = QStringLiteral("'%1' is not an ELF object (file too small)").arg(QDir::toNativeSeparators(m_binary));
+ return NotElf;
+ }
+
+ if (strncmp(mapper.start, "\177ELF", 4) != 0) {
+ m_errorString = QStringLiteral("'%1' is not an ELF object").arg(QDir::toNativeSeparators(m_binary));
+ return NotElf;
+ }
+
+ // 32 or 64 bit
+ m_elfData.elfclass = ElfClass(mapper.start[4]);
+ const bool is64Bit = m_elfData.elfclass == Elf_ELFCLASS64;
+ if (m_elfData.elfclass != Elf_ELFCLASS32 && m_elfData.elfclass != Elf_ELFCLASS64) {
+ m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("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::QStringLiteral("'%1' is an invalid ELF object (%2)")
+ // .arg(m_binary).arg(QLatin1String("wrong cpu architecture"));
+ // return Corrupt;
+ // }
+
+ // Read Endianhness.
+ m_elfData.endian = ElfEndian(mapper.ustart[5]);
+ if (m_elfData.endian != Elf_ELFDATA2LSB && m_elfData.endian != Elf_ELFDATA2MSB) {
+ m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("odd endianness"));
+ return Corrupt;
+ }
+
+ const uchar *data = mapper.ustart + 16; // e_ident
+ m_elfData.elftype = ElfType(getHalfWord(data, m_elfData));
+ m_elfData.elfmachine = ElfMachine(getHalfWord(data, m_elfData));
+ /* e_version = */ getWord(data, m_elfData);
+ m_elfData.entryPoint = getAddress(data, m_elfData);
+
+ quint64 e_phoff = getOffset(data, m_elfData);
+ quint64 e_shoff = getOffset(data, m_elfData);
+ /* e_flags = */ getWord(data, m_elfData);
+
+ quint32 e_shsize = getHalfWord(data, m_elfData);
+
+ if (e_shsize > fdlen) {
+ m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_shsize"));
+ return Corrupt;
+ }
+
+ quint32 e_phentsize = getHalfWord(data, m_elfData);
+ if (e_phentsize != (is64Bit ? 56 : 32)) {
+ m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("invalid structure"));
+ return ElfReader::Corrupt;
+ }
+ quint32 e_phnum = getHalfWord(data, m_elfData);
+
+ quint32 e_shentsize = getHalfWord(data, m_elfData);
+
+ if (e_shentsize % 4) {
+ m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_shentsize"));
+ return Corrupt;
+ }
+
+ quint32 e_shnum = getHalfWord(data, m_elfData);
+ quint32 e_shtrndx = getHalfWord(data, m_elfData);
+ if (data != mapper.ustart + (is64Bit ? 64 : 52)) {
+ m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_phentsize"));
+ return ElfReader::Corrupt;
+ }
+
+ if (quint64(e_shnum) * e_shentsize > fdlen) {
+ const QString reason = QStringLiteral("announced %1 sections, each %2 bytes, exceed file size").arg(e_shnum).arg(e_shentsize);
+ m_errorString = msgInvalidElfObject(m_binary, reason);
+ return Corrupt;
+ }
+
+ quint64 soff = e_shoff + e_shentsize * e_shtrndx;
+
+// if ((soff + e_shentsize) > fdlen || soff % 4 || soff == 0) {
+// m_errorString = QLibrary::QStringLiteral("'%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;
+// }
+
+ if (e_shoff) {
+ ElfSectionHeader strtab;
+ parseSectionHeader(mapper.ustart + soff, &strtab, m_elfData);
+ const quint64 stringTableFileOffset = strtab.offset;
+ if (quint32(stringTableFileOffset + e_shentsize) >= fdlen
+ || stringTableFileOffset == 0) {
+ const QString reason = QStringLiteral("string table seems to be at 0x%1").arg(soff, 0, 16);
+ m_errorString = msgInvalidElfObject(m_binary, reason);
+ return Corrupt;
+ }
+
+ for (quint32 i = 0; i < e_shnum; ++i) {
+ const uchar *s = mapper.ustart + e_shoff + i * e_shentsize;
+ ElfSectionHeader sh;
+ parseSectionHeader(s, &sh, m_elfData);
+
+ if (stringTableFileOffset + sh.index > fdlen) {
+ const QString reason = QStringLiteral("section name %1 of %2 behind end of file")
+ .arg(i).arg(e_shnum);
+ m_errorString = msgInvalidElfObject(m_binary, reason);
+ return Corrupt;
+ }
+
+ sh.name = mapper.start + stringTableFileOffset + sh.index;
+ if (sh.name == ".gdb_index") {
+ m_elfData.symbolsType = FastSymbols;
+ } else if (sh.name == ".debug_info") {
+ m_elfData.symbolsType = PlainSymbols;
+ } else if (sh.name == ".gnu_debuglink") {
+ m_elfData.debugLink = QByteArray(mapper.start + sh.offset);
+ m_elfData.symbolsType = LinkedSymbols;
+ } else if (sh.name == ".note.gnu.build-id") {
+ m_elfData.symbolsType = BuildIdSymbols;
+ if (sh.size > 16)
+ m_elfData.buildId = QByteArray(mapper.start + sh.offset + 16,
+ int(sh.size) - 16).toHex();
+ }
+ m_elfData.sectionHeaders.append(sh);
+ }
+ }
+
+ if (e_phoff) {
+ for (quint32 i = 0; i < e_phnum; ++i) {
+ const uchar *s = mapper.ustart + e_phoff + i * e_phentsize;
+ ElfProgramHeader ph;
+ parseProgramHeader(s, &ph, m_elfData);
+ m_elfData.programHeaders.append(ph);
+ }
+ }
+ return Ok;
+}
+
+QByteArray ElfReader::readSection(const QByteArray &name)
+{
+ readIt();
+ int i = m_elfData.indexOf(name);
+ if (i == -1)
+ return QByteArray();
+
+ ElfMapper mapper(this);
+ if (!mapper.map())
+ return QByteArray();
+
+ const ElfSectionHeader &section = m_elfData.sectionHeaders.at(i);
+ return QByteArray(mapper.start + section.offset, int(section.size));
+}
+
+static QByteArray cutout(const char *s)
+{
+ QByteArray res(s, 80);
+ const int pos = res.indexOf('\0');
+ if (pos != -1)
+ res.resize(pos - 1);
+ return res;
+}
+
+QByteArray ElfReader::readCoreName(bool *isCore)
+{
+ *isCore = false;
+
+ readIt();
+
+ ElfMapper mapper(this);
+ if (!mapper.map())
+ return QByteArray();
+
+ if (m_elfData.elftype != Elf_ET_CORE)
+ return QByteArray();
+
+ *isCore = true;
+
+ for (int i = 0, n = m_elfData.sectionHeaders.size(); i != n; ++i)
+ if (m_elfData.sectionHeaders.at(i).type == Elf_SHT_NOTE) {
+ const ElfSectionHeader &header = m_elfData.sectionHeaders.at(i);
+ return cutout(mapper.start + header.offset + 0x40);
+ }
+
+ for (int i = 0, n = m_elfData.programHeaders.size(); i != n; ++i)
+ if (m_elfData.programHeaders.at(i).type == Elf_PT_NOTE) {
+ const ElfProgramHeader &header = m_elfData.programHeaders.at(i);
+ return cutout(mapper.start + header.offset + 0xec);
+ }
+
+ return QByteArray();
+}
+
+int ElfData::indexOf(const QByteArray &name) const
+{
+ for (int i = 0, n = sectionHeaders.size(); i != n; ++i)
+ if (sectionHeaders.at(i).name == name)
+ return i;
+ return -1;
+}
+
+/* Helpers for reading out the .dynamic section containing the dependencies.
+ * The ".dynamic" section is an array of
+ * typedef struct {
+ * Elf32_Sword d_tag;
+ * union {
+ * Elf32_Word d_val;
+ * dElf32_Addr d_ptr;
+ * } d_un;
+ * } Elf32_Dyn
+ * with entries where a tag DT_NEEDED indicates that m_val is an offset into
+ * the string table ".dynstr". The documentation states that entries with the
+ * tag DT_STRTAB contain an offset for the string table to be used, but that
+ * has been found not to contain valid entries. */
+
+enum DynamicSectionTags {
+ DT_NULL = 0,
+ DT_NEEDED = 1,
+ DT_STRTAB = 5,
+ DT_SONAME = 14,
+ DT_RPATH = 15
+};
+
+QList<QByteArray> ElfReader::dependencies()
+{
+ QList<QByteArray> result;
+
+ ElfMapper mapper(this);
+ if (!mapper.map()) {
+ m_errorString = QStringLiteral("Mapper failure");
+ return result;
+ }
+ quint64 dynStrOffset = 0;
+ quint64 dynamicOffset = 0;
+ quint64 dynamicSize = 0;
+
+ const QList<ElfSectionHeader> &headers = readHeaders().sectionHeaders;
+ for (const ElfSectionHeader &eh : headers) {
+ if (eh.name == QByteArrayLiteral(".dynstr")) {
+ dynStrOffset = eh.offset;
+ } else if (eh.name == QByteArrayLiteral(".dynamic")) {
+ dynamicOffset = eh.offset;
+ dynamicSize = eh.size;
+ }
+ if (dynStrOffset && dynamicOffset)
+ break;
+ }
+
+ if (!dynStrOffset || !dynamicOffset) {
+ m_errorString = QStringLiteral("Not a dynamically linked executable.");
+ return result;
+ }
+
+ const unsigned char *dynamicData = mapper.ustart + dynamicOffset;
+ const unsigned char *dynamicDataEnd = dynamicData + dynamicSize;
+ while (dynamicData < dynamicDataEnd) {
+ const quint32 tag = getWord(dynamicData, m_elfData);
+ if (tag == DT_NULL)
+ break;
+ if (m_elfData.elfclass == Elf_ELFCLASS64)
+ dynamicData += sizeof(quint32); // padding to d_val/d_ptr.
+ if (tag == DT_NEEDED) {
+ const quint32 offset = getWord(dynamicData, m_elfData);
+ if (m_elfData.elfclass == Elf_ELFCLASS64)
+ dynamicData += sizeof(quint32); // past d_ptr.
+ const char *name = mapper.start + dynStrOffset + offset;
+ result.push_back(name);
+ } else {
+ dynamicData += m_elfData.elfclass == Elf_ELFCLASS64 ? 8 : 4;
+ }
+ }
+ return result;
+}
+
+QT_END_NAMESPACE