1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "elfreader.h"
30 
31 #include <QDir>
32 
33 QT_BEGIN_NAMESPACE
34 
35 /* This is a copy of the ELF reader contained in Qt Creator (src/libs/utils),
36  * extended by the dependencies() function to read out the dependencies of a dynamic executable. */
37 
getHalfWord(const unsigned char * & s,const ElfData & context)38 quint16 getHalfWord(const unsigned char *&s, const ElfData &context)
39 {
40     quint16 res;
41     if (context.endian == Elf_ELFDATA2MSB)
42         res = qFromBigEndian<quint16>(s);
43     else
44         res = qFromLittleEndian<quint16>(s);
45     s += 2;
46     return res;
47 }
48 
getWord(const unsigned char * & s,const ElfData & context)49 quint32 getWord(const unsigned char *&s, const ElfData &context)
50 {
51     quint32 res;
52     if (context.endian == Elf_ELFDATA2MSB)
53         res = qFromBigEndian<quint32>(s);
54     else
55         res = qFromLittleEndian<quint32>(s);
56     s += 4;
57     return res;
58 }
59 
getAddress(const unsigned char * & s,const ElfData & context)60 quint64 getAddress(const unsigned char *&s, const ElfData &context)
61 {
62     quint64 res;
63     if (context.elfclass == Elf_ELFCLASS32) {
64         if (context.endian == Elf_ELFDATA2MSB)
65             res = qFromBigEndian<quint32>(s);
66         else
67             res = qFromLittleEndian<quint32>(s);
68         s += 4;
69     } else {
70         if (context.endian == Elf_ELFDATA2MSB)
71             res = qFromBigEndian<quint64>(s);
72         else
73             res = qFromLittleEndian<quint64>(s);
74         s += 8;
75     }
76     return res;
77 }
78 
getOffset(const unsigned char * & s,const ElfData & context)79 quint64 getOffset(const unsigned char *&s, const ElfData &context)
80 {
81     return getAddress(s, context);
82 }
83 
parseSectionHeader(const uchar * s,ElfSectionHeader * sh,const ElfData & context)84 static void parseSectionHeader(const uchar *s, ElfSectionHeader *sh, const ElfData &context)
85 {
86     sh->index = getWord(s, context);
87     sh->type = getWord(s, context);
88     sh->flags = quint32(getOffset(s, context));
89     sh->addr = getAddress(s, context);
90     sh->offset = getOffset(s, context);
91     sh->size = getOffset(s, context);
92 }
93 
parseProgramHeader(const uchar * s,ElfProgramHeader * sh,const ElfData & context)94 static void parseProgramHeader(const uchar *s, ElfProgramHeader *sh, const ElfData &context)
95 {
96     sh->type = getWord(s, context);
97     sh->offset = getOffset(s, context);
98     /* p_vaddr = */ getAddress(s, context);
99     /* p_paddr = */ getAddress(s, context);
100     sh->filesz = getWord(s, context);
101     sh->memsz = getWord(s, context);
102 }
103 
104 class ElfMapper
105 {
106 public:
ElfMapper(const ElfReader * reader)107     ElfMapper(const ElfReader *reader) : file(reader->m_binary) {}
108 
map()109     bool map()
110     {
111         if (!file.open(QIODevice::ReadOnly))
112             return false;
113 
114         fdlen = quint64(file.size());
115         ustart = file.map(0, qint64(fdlen));
116         if (ustart == 0) {
117             // Try reading the data into memory instead.
118             raw = file.readAll();
119             start = raw.constData();
120             fdlen = quint64(raw.size());
121         }
122         return true;
123     }
124 
125 public:
126     QFile file;
127     QByteArray raw;
128     union { const char *start; const uchar *ustart; };
129     quint64 fdlen;
130 };
131 
ElfReader(const QString & binary)132 ElfReader::ElfReader(const QString &binary)
133     : m_binary(binary)
134 {
135 }
136 
readHeaders()137 ElfData ElfReader::readHeaders()
138 {
139     readIt();
140     return m_elfData;
141 }
142 
msgInvalidElfObject(const QString & binary,const QString & why)143 static inline QString msgInvalidElfObject(const QString &binary, const QString &why)
144 {
145     return QStringLiteral("'%1' is an invalid ELF object (%2)")
146            .arg(QDir::toNativeSeparators(binary), why);
147 }
148 
readIt()149 ElfReader::Result ElfReader::readIt()
150 {
151     if (!m_elfData.sectionHeaders.isEmpty())
152         return Ok;
153     if (!m_elfData.programHeaders.isEmpty())
154         return Ok;
155 
156     ElfMapper mapper(this);
157     if (!mapper.map())
158         return Corrupt;
159 
160     const quint64 fdlen = mapper.fdlen;
161 
162     if (fdlen < 64) {
163         m_errorString = QStringLiteral("'%1' is not an ELF object (file too small)").arg(QDir::toNativeSeparators(m_binary));
164         return NotElf;
165     }
166 
167     if (strncmp(mapper.start, "\177ELF", 4) != 0) {
168         m_errorString = QStringLiteral("'%1' is not an ELF object").arg(QDir::toNativeSeparators(m_binary));
169         return NotElf;
170     }
171 
172     // 32 or 64 bit
173     m_elfData.elfclass = ElfClass(mapper.start[4]);
174     const bool is64Bit = m_elfData.elfclass == Elf_ELFCLASS64;
175     if (m_elfData.elfclass != Elf_ELFCLASS32 && m_elfData.elfclass != Elf_ELFCLASS64) {
176         m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("odd cpu architecture"));
177         return Corrupt;
178     }
179 
180     // int bits = (data[4] << 5);
181     // If you remove this check to read ELF objects of a different arch,
182     // please make sure you modify the typedefs
183     // to match the _plugin_ architecture.
184     // if ((sizeof(void*) == 4 && bits != 32)
185     //     || (sizeof(void*) == 8 && bits != 64)) {
186     //     if (errorString)
187     //         *errorString = QLibrary::QStringLiteral("'%1' is an invalid ELF object (%2)")
188     //         .arg(m_binary).arg(QLatin1String("wrong cpu architecture"));
189     //     return Corrupt;
190     // }
191 
192     // Read Endianhness.
193     m_elfData.endian = ElfEndian(mapper.ustart[5]);
194     if (m_elfData.endian != Elf_ELFDATA2LSB && m_elfData.endian != Elf_ELFDATA2MSB) {
195         m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("odd endianness"));
196         return Corrupt;
197     }
198 
199     const uchar *data = mapper.ustart + 16; // e_ident
200     m_elfData.elftype    = ElfType(getHalfWord(data, m_elfData));
201     m_elfData.elfmachine = ElfMachine(getHalfWord(data, m_elfData));
202     /* e_version = */   getWord(data, m_elfData);
203     m_elfData.entryPoint = getAddress(data, m_elfData);
204 
205     quint64 e_phoff   = getOffset(data, m_elfData);
206     quint64 e_shoff   = getOffset(data, m_elfData);
207     /* e_flags = */     getWord(data, m_elfData);
208 
209     quint32 e_shsize  = getHalfWord(data, m_elfData);
210 
211     if (e_shsize > fdlen) {
212         m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_shsize"));
213         return Corrupt;
214     }
215 
216     quint32 e_phentsize = getHalfWord(data, m_elfData);
217     if (e_phentsize != (is64Bit ? 56 : 32)) {
218         m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("invalid structure"));
219         return ElfReader::Corrupt;
220     }
221     quint32 e_phnum     = getHalfWord(data, m_elfData);
222 
223     quint32 e_shentsize = getHalfWord(data, m_elfData);
224 
225     if (e_shentsize % 4) {
226         m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_shentsize"));
227         return Corrupt;
228     }
229 
230     quint32 e_shnum     = getHalfWord(data, m_elfData);
231     quint32 e_shtrndx   = getHalfWord(data, m_elfData);
232     if (data != mapper.ustart + (is64Bit ? 64 : 52)) {
233         m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_phentsize"));
234         return ElfReader::Corrupt;
235     }
236 
237     if (quint64(e_shnum) * e_shentsize > fdlen) {
238         const QString reason = QStringLiteral("announced %1 sections, each %2 bytes, exceed file size").arg(e_shnum).arg(e_shentsize);
239         m_errorString = msgInvalidElfObject(m_binary, reason);
240         return Corrupt;
241     }
242 
243     quint64 soff = e_shoff + e_shentsize * e_shtrndx;
244 
245 //    if ((soff + e_shentsize) > fdlen || soff % 4 || soff == 0) {
246 //        m_errorString = QLibrary::QStringLiteral("'%1' is an invalid ELF object (%2)")
247 //           .arg(m_binary)
248 //           .arg(QLatin1String("shstrtab section header seems to be at %1"))
249 //           .arg(QString::number(soff, 16));
250 //        return Corrupt;
251 //    }
252 
253     if (e_shoff) {
254         ElfSectionHeader strtab;
255         parseSectionHeader(mapper.ustart + soff, &strtab, m_elfData);
256         const quint64 stringTableFileOffset = strtab.offset;
257         if (quint32(stringTableFileOffset + e_shentsize) >= fdlen
258                 || stringTableFileOffset == 0) {
259             const QString reason = QStringLiteral("string table seems to be at 0x%1").arg(soff, 0, 16);
260             m_errorString = msgInvalidElfObject(m_binary, reason);
261             return Corrupt;
262         }
263 
264         for (quint32 i = 0; i < e_shnum; ++i) {
265             const uchar *s = mapper.ustart + e_shoff + i * e_shentsize;
266             ElfSectionHeader sh;
267             parseSectionHeader(s, &sh, m_elfData);
268 
269             if (stringTableFileOffset + sh.index > fdlen) {
270                 const QString reason = QStringLiteral("section name %1 of %2 behind end of file")
271                                        .arg(i).arg(e_shnum);
272                 m_errorString = msgInvalidElfObject(m_binary, reason);
273                 return Corrupt;
274             }
275 
276             sh.name = mapper.start + stringTableFileOffset + sh.index;
277             if (sh.name == ".gdb_index") {
278                 m_elfData.symbolsType = FastSymbols;
279             } else if (sh.name == ".debug_info") {
280                 m_elfData.symbolsType = PlainSymbols;
281             } else if (sh.name == ".gnu_debuglink") {
282                 m_elfData.debugLink = QByteArray(mapper.start + sh.offset);
283                 m_elfData.symbolsType = LinkedSymbols;
284             } else if (sh.name == ".note.gnu.build-id") {
285                 m_elfData.symbolsType = BuildIdSymbols;
286                 if (sh.size > 16)
287                     m_elfData.buildId = QByteArray(mapper.start + sh.offset + 16,
288                                                    int(sh.size) - 16).toHex();
289             }
290             m_elfData.sectionHeaders.append(sh);
291         }
292     }
293 
294     if (e_phoff) {
295         for (quint32 i = 0; i < e_phnum; ++i) {
296             const uchar *s = mapper.ustart + e_phoff + i * e_phentsize;
297             ElfProgramHeader ph;
298             parseProgramHeader(s, &ph, m_elfData);
299             m_elfData.programHeaders.append(ph);
300         }
301     }
302     return Ok;
303 }
304 
readSection(const QByteArray & name)305 QByteArray ElfReader::readSection(const QByteArray &name)
306 {
307     readIt();
308     int i = m_elfData.indexOf(name);
309     if (i == -1)
310         return QByteArray();
311 
312     ElfMapper mapper(this);
313     if (!mapper.map())
314         return QByteArray();
315 
316     const ElfSectionHeader &section = m_elfData.sectionHeaders.at(i);
317     return QByteArray(mapper.start + section.offset, int(section.size));
318 }
319 
cutout(const char * s)320 static QByteArray cutout(const char *s)
321 {
322     QByteArray res(s, 80);
323     const int pos = res.indexOf('\0');
324     if (pos != -1)
325         res.resize(pos - 1);
326     return res;
327 }
328 
readCoreName(bool * isCore)329 QByteArray ElfReader::readCoreName(bool *isCore)
330 {
331     *isCore = false;
332 
333     readIt();
334 
335     ElfMapper mapper(this);
336     if (!mapper.map())
337         return QByteArray();
338 
339     if (m_elfData.elftype != Elf_ET_CORE)
340         return QByteArray();
341 
342     *isCore = true;
343 
344     for (int i = 0, n = m_elfData.sectionHeaders.size(); i != n; ++i)
345         if (m_elfData.sectionHeaders.at(i).type == Elf_SHT_NOTE) {
346             const ElfSectionHeader &header = m_elfData.sectionHeaders.at(i);
347             return cutout(mapper.start + header.offset + 0x40);
348         }
349 
350     for (int i = 0, n = m_elfData.programHeaders.size(); i != n; ++i)
351         if (m_elfData.programHeaders.at(i).type == Elf_PT_NOTE) {
352             const ElfProgramHeader &header = m_elfData.programHeaders.at(i);
353             return cutout(mapper.start + header.offset + 0xec);
354         }
355 
356     return QByteArray();
357 }
358 
indexOf(const QByteArray & name) const359 int ElfData::indexOf(const QByteArray &name) const
360 {
361     for (int i = 0, n = sectionHeaders.size(); i != n; ++i)
362         if (sectionHeaders.at(i).name == name)
363             return i;
364     return -1;
365 }
366 
367 /* Helpers for reading out the .dynamic section containing the dependencies.
368  * The ".dynamic" section is an array of
369  * typedef struct {
370  *      Elf32_Sword d_tag;
371  *      union {
372  *          Elf32_Word  d_val;
373  *          dElf32_Addr d_ptr;
374  *     } d_un;
375  *  } Elf32_Dyn
376  * with entries where a tag DT_NEEDED indicates that m_val is an offset into
377  * the string table ".dynstr". The documentation states that entries with the
378  * tag DT_STRTAB contain an offset for the string table to be used, but that
379  * has been found not to contain valid entries. */
380 
381 enum DynamicSectionTags {
382     DT_NULL = 0,
383     DT_NEEDED = 1,
384     DT_STRTAB = 5,
385     DT_SONAME = 14,
386     DT_RPATH = 15
387 };
388 
dependencies()389 QList<QByteArray> ElfReader::dependencies()
390 {
391     QList<QByteArray> result;
392 
393     ElfMapper mapper(this);
394     if (!mapper.map()) {
395         m_errorString = QStringLiteral("Mapper failure");
396         return result;
397     }
398     quint64 dynStrOffset = 0;
399     quint64 dynamicOffset = 0;
400     quint64 dynamicSize = 0;
401 
402     const QVector<ElfSectionHeader> &headers = readHeaders().sectionHeaders;
403     for (const ElfSectionHeader &eh : headers) {
404         if (eh.name  == QByteArrayLiteral(".dynstr")) {
405             dynStrOffset = eh.offset;
406         } else if (eh.name  == QByteArrayLiteral(".dynamic")) {
407             dynamicOffset = eh.offset;
408             dynamicSize = eh.size;
409         }
410         if (dynStrOffset && dynamicOffset)
411             break;
412     }
413 
414     if (!dynStrOffset || !dynamicOffset) {
415         m_errorString = QStringLiteral("Not a dynamically linked executable.");
416         return result;
417     }
418 
419     const unsigned char *dynamicData = mapper.ustart + dynamicOffset;
420     const unsigned char *dynamicDataEnd = dynamicData + dynamicSize;
421     while (dynamicData < dynamicDataEnd) {
422         const quint32 tag = getWord(dynamicData, m_elfData);
423         if (tag == DT_NULL)
424             break;
425         if (m_elfData.elfclass == Elf_ELFCLASS64)
426             dynamicData += sizeof(quint32); // padding to d_val/d_ptr.
427         if (tag == DT_NEEDED) {
428             const quint32 offset = getWord(dynamicData, m_elfData);
429             if (m_elfData.elfclass == Elf_ELFCLASS64)
430                 dynamicData += sizeof(quint32); // past d_ptr.
431             const char *name = mapper.start + dynStrOffset + offset;
432             result.push_back(name);
433         } else {
434              dynamicData += m_elfData.elfclass == Elf_ELFCLASS64 ? 8 : 4;
435         }
436     }
437     return result;
438 }
439 
440 QT_END_NAMESPACE
441