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 §ion = 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