Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
elfreader.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "elfreader.h"
5
6#include <QDir>
7
9
10using namespace Qt::StringLiterals;
11
12/* This is a copy of the ELF reader contained in Qt Creator (src/libs/utils),
13 * extended by the dependencies() function to read out the dependencies of a dynamic executable. */
14
15quint16 getHalfWord(const unsigned char *&s, const ElfData &context)
16{
18 if (context.endian == Elf_ELFDATA2MSB)
19 res = qFromBigEndian<quint16>(s);
20 else
21 res = qFromLittleEndian<quint16>(s);
22 s += 2;
23 return res;
24}
25
26quint32 getWord(const unsigned char *&s, const ElfData &context)
27{
29 if (context.endian == Elf_ELFDATA2MSB)
30 res = qFromBigEndian<quint32>(s);
31 else
32 res = qFromLittleEndian<quint32>(s);
33 s += 4;
34 return res;
35}
36
37quint64 getAddress(const unsigned char *&s, const ElfData &context)
38{
40 if (context.elfclass == Elf_ELFCLASS32) {
41 if (context.endian == Elf_ELFDATA2MSB)
42 res = qFromBigEndian<quint32>(s);
43 else
44 res = qFromLittleEndian<quint32>(s);
45 s += 4;
46 } else {
47 if (context.endian == Elf_ELFDATA2MSB)
48 res = qFromBigEndian<quint64>(s);
49 else
50 res = qFromLittleEndian<quint64>(s);
51 s += 8;
52 }
53 return res;
54}
55
56quint64 getOffset(const unsigned char *&s, const ElfData &context)
57{
58 return getAddress(s, context);
59}
60
61static void parseSectionHeader(const uchar *s, ElfSectionHeader *sh, const ElfData &context)
62{
63 sh->index = getWord(s, context);
64 sh->type = getWord(s, context);
66 sh->addr = getAddress(s, context);
67 sh->offset = getOffset(s, context);
68 sh->size = getOffset(s, context);
69}
70
71static void parseProgramHeader(const uchar *s, ElfProgramHeader *sh, const ElfData &context)
72{
73 sh->type = getWord(s, context);
74 sh->offset = getOffset(s, context);
75 /* p_vaddr = */ getAddress(s, context);
76 /* p_paddr = */ getAddress(s, context);
77 sh->filesz = getWord(s, context);
78 sh->memsz = getWord(s, context);
79}
80
82{
83public:
84 ElfMapper(const ElfReader *reader) : file(reader->m_binary) {}
85
86 bool map()
87 {
89 return false;
90
92 ustart = file.map(0, qint64(fdlen));
93 if (ustart == 0) {
94 // Try reading the data into memory instead.
95 raw = file.readAll();
97 fdlen = quint64(raw.size());
98 }
99 return true;
100 }
101
102public:
105 union { const char *start; const uchar *ustart; };
107};
108
110 : m_binary(binary)
111{
112}
113
115{
116 readIt();
117 return m_elfData;
118}
119
120static inline QString msgInvalidElfObject(const QString &binary, const QString &why)
121{
122 return QStringLiteral("'%1' is an invalid ELF object (%2)")
124}
125
126ElfReader::Result ElfReader::readIt()
127{
128 if (!m_elfData.sectionHeaders.isEmpty())
129 return Ok;
130 if (!m_elfData.programHeaders.isEmpty())
131 return Ok;
132
133 ElfMapper mapper(this);
134 if (!mapper.map())
135 return Corrupt;
136
137 const quint64 fdlen = mapper.fdlen;
138
139 if (fdlen < 64) {
140 m_errorString = QStringLiteral("'%1' is not an ELF object (file too small)").arg(QDir::toNativeSeparators(m_binary));
141 return NotElf;
142 }
143
144 if (strncmp(mapper.start, "\177ELF", 4) != 0) {
145 m_errorString = QStringLiteral("'%1' is not an ELF object").arg(QDir::toNativeSeparators(m_binary));
146 return NotElf;
147 }
148
149 // 32 or 64 bit
150 m_elfData.elfclass = ElfClass(mapper.start[4]);
151 const bool is64Bit = m_elfData.elfclass == Elf_ELFCLASS64;
152 if (m_elfData.elfclass != Elf_ELFCLASS32 && m_elfData.elfclass != Elf_ELFCLASS64) {
153 m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("odd cpu architecture"));
154 return Corrupt;
155 }
156
157 // int bits = (data[4] << 5);
158 // If you remove this check to read ELF objects of a different arch,
159 // please make sure you modify the typedefs
160 // to match the _plugin_ architecture.
161 // if ((sizeof(void*) == 4 && bits != 32)
162 // || (sizeof(void*) == 8 && bits != 64)) {
163 // if (errorString)
164 // *errorString = QLibrary::QStringLiteral("'%1' is an invalid ELF object (%2)")
165 // .arg(m_binary).arg("wrong cpu architecture"_L1);
166 // return Corrupt;
167 // }
168
169 // Read Endianhness.
170 m_elfData.endian = ElfEndian(mapper.ustart[5]);
171 if (m_elfData.endian != Elf_ELFDATA2LSB && m_elfData.endian != Elf_ELFDATA2MSB) {
172 m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("odd endianness"));
173 return Corrupt;
174 }
175
176 const uchar *data = mapper.ustart + 16; // e_ident
177 m_elfData.elftype = ElfType(getHalfWord(data, m_elfData));
178 m_elfData.elfmachine = ElfMachine(getHalfWord(data, m_elfData));
179 /* e_version = */ getWord(data, m_elfData);
180 m_elfData.entryPoint = getAddress(data, m_elfData);
181
182 quint64 e_phoff = getOffset(data, m_elfData);
183 quint64 e_shoff = getOffset(data, m_elfData);
184 /* e_flags = */ getWord(data, m_elfData);
185
186 quint32 e_shsize = getHalfWord(data, m_elfData);
187
188 if (e_shsize > fdlen) {
189 m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_shsize"));
190 return Corrupt;
191 }
192
193 quint32 e_phentsize = getHalfWord(data, m_elfData);
194 if (e_phentsize != (is64Bit ? 56 : 32)) {
195 m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("invalid structure"));
196 return ElfReader::Corrupt;
197 }
198 quint32 e_phnum = getHalfWord(data, m_elfData);
199
200 quint32 e_shentsize = getHalfWord(data, m_elfData);
201
202 if (e_shentsize % 4) {
203 m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_shentsize"));
204 return Corrupt;
205 }
206
207 quint32 e_shnum = getHalfWord(data, m_elfData);
208 quint32 e_shtrndx = getHalfWord(data, m_elfData);
209 if (data != mapper.ustart + (is64Bit ? 64 : 52)) {
210 m_errorString = msgInvalidElfObject(m_binary, QStringLiteral("unexpected e_phentsize"));
211 return ElfReader::Corrupt;
212 }
213
214 if (quint64(e_shnum) * e_shentsize > fdlen) {
215 const QString reason = QStringLiteral("announced %1 sections, each %2 bytes, exceed file size").arg(e_shnum).arg(e_shentsize);
216 m_errorString = msgInvalidElfObject(m_binary, reason);
217 return Corrupt;
218 }
219
220 quint64 soff = e_shoff + e_shentsize * e_shtrndx;
221
222// if ((soff + e_shentsize) > fdlen || soff % 4 || soff == 0) {
223// m_errorString = QLibrary::QStringLiteral("'%1' is an invalid ELF object (%2)")
224// .arg(m_binary)
225// .arg("shstrtab section header seems to be at %1"_L1)
226// .arg(QString::number(soff, 16));
227// return Corrupt;
228// }
229
230 if (e_shoff) {
231 ElfSectionHeader strtab;
232 parseSectionHeader(mapper.ustart + soff, &strtab, m_elfData);
233 const quint64 stringTableFileOffset = strtab.offset;
234 if (quint32(stringTableFileOffset + e_shentsize) >= fdlen
235 || stringTableFileOffset == 0) {
236 const QString reason = QStringLiteral("string table seems to be at 0x%1").arg(soff, 0, 16);
237 m_errorString = msgInvalidElfObject(m_binary, reason);
238 return Corrupt;
239 }
240
241 for (quint32 i = 0; i < e_shnum; ++i) {
242 const uchar *s = mapper.ustart + e_shoff + i * e_shentsize;
244 parseSectionHeader(s, &sh, m_elfData);
245
246 if (stringTableFileOffset + sh.index > fdlen) {
247 const QString reason = QStringLiteral("section name %1 of %2 behind end of file")
248 .arg(i).arg(e_shnum);
249 m_errorString = msgInvalidElfObject(m_binary, reason);
250 return Corrupt;
251 }
252
253 sh.name = mapper.start + stringTableFileOffset + sh.index;
254 if (sh.name == ".gdb_index") {
255 m_elfData.symbolsType = FastSymbols;
256 } else if (sh.name == ".debug_info") {
257 m_elfData.symbolsType = PlainSymbols;
258 } else if (sh.name == ".gnu_debuglink") {
259 m_elfData.debugLink = QByteArray(mapper.start + sh.offset);
260 m_elfData.symbolsType = LinkedSymbols;
261 } else if (sh.name == ".note.gnu.build-id") {
262 m_elfData.symbolsType = BuildIdSymbols;
263 if (sh.size > 16)
264 m_elfData.buildId = QByteArray(mapper.start + sh.offset + 16,
265 int(sh.size) - 16).toHex();
266 }
267 m_elfData.sectionHeaders.append(sh);
268 }
269 }
270
271 if (e_phoff) {
272 for (quint32 i = 0; i < e_phnum; ++i) {
273 const uchar *s = mapper.ustart + e_phoff + i * e_phentsize;
275 parseProgramHeader(s, &ph, m_elfData);
276 m_elfData.programHeaders.append(ph);
277 }
278 }
279 return Ok;
280}
281
283{
284 readIt();
285 int i = m_elfData.indexOf(name);
286 if (i == -1)
287 return QByteArray();
288
289 ElfMapper mapper(this);
290 if (!mapper.map())
291 return QByteArray();
292
293 const ElfSectionHeader &section = m_elfData.sectionHeaders.at(i);
294 return QByteArray(mapper.start + section.offset, int(section.size));
295}
296
297static QByteArray cutout(const char *s)
298{
299 QByteArray res(s, 80);
300 const int pos = res.indexOf('\0');
301 if (pos != -1)
302 res.resize(pos - 1);
303 return res;
304}
305
307{
308 *isCore = false;
309
310 readIt();
311
312 ElfMapper mapper(this);
313 if (!mapper.map())
314 return QByteArray();
315
316 if (m_elfData.elftype != Elf_ET_CORE)
317 return QByteArray();
318
319 *isCore = true;
320
321 for (int i = 0, n = m_elfData.sectionHeaders.size(); i != n; ++i)
322 if (m_elfData.sectionHeaders.at(i).type == Elf_SHT_NOTE) {
323 const ElfSectionHeader &header = m_elfData.sectionHeaders.at(i);
324 return cutout(mapper.start + header.offset + 0x40);
325 }
326
327 for (int i = 0, n = m_elfData.programHeaders.size(); i != n; ++i)
328 if (m_elfData.programHeaders.at(i).type == Elf_PT_NOTE) {
329 const ElfProgramHeader &header = m_elfData.programHeaders.at(i);
330 return cutout(mapper.start + header.offset + 0xec);
331 }
332
333 return QByteArray();
334}
335
337{
338 for (int i = 0, n = sectionHeaders.size(); i != n; ++i)
339 if (sectionHeaders.at(i).name == name)
340 return i;
341 return -1;
342}
343
344/* Helpers for reading out the .dynamic section containing the dependencies.
345 * The ".dynamic" section is an array of
346 * typedef struct {
347 * Elf32_Sword d_tag;
348 * union {
349 * Elf32_Word d_val;
350 * dElf32_Addr d_ptr;
351 * } d_un;
352 * } Elf32_Dyn
353 * with entries where a tag DT_NEEDED indicates that m_val is an offset into
354 * the string table ".dynstr". The documentation states that entries with the
355 * tag DT_STRTAB contain an offset for the string table to be used, but that
356 * has been found not to contain valid entries. */
357
363 DT_RPATH = 15
365
367{
369
370 ElfMapper mapper(this);
371 if (!mapper.map()) {
372 m_errorString = QStringLiteral("Mapper failure");
373 return result;
374 }
375 quint64 dynStrOffset = 0;
376 quint64 dynamicOffset = 0;
377 quint64 dynamicSize = 0;
378
380 for (const ElfSectionHeader &eh : headers) {
381 if (eh.name == QByteArrayLiteral(".dynstr")) {
382 dynStrOffset = eh.offset;
383 } else if (eh.name == QByteArrayLiteral(".dynamic")) {
384 dynamicOffset = eh.offset;
385 dynamicSize = eh.size;
386 }
387 if (dynStrOffset && dynamicOffset)
388 break;
389 }
390
391 if (!dynStrOffset || !dynamicOffset) {
392 m_errorString = QStringLiteral("Not a dynamically linked executable.");
393 return result;
394 }
395
396 const unsigned char *dynamicData = mapper.ustart + dynamicOffset;
397 const unsigned char *dynamicDataEnd = dynamicData + dynamicSize;
398 while (dynamicData < dynamicDataEnd) {
399 const quint32 tag = getWord(dynamicData, m_elfData);
400 if (tag == DT_NULL)
401 break;
402 if (m_elfData.elfclass == Elf_ELFCLASS64)
403 dynamicData += sizeof(quint32); // padding to d_val/d_ptr.
404 if (tag == DT_NEEDED) {
405 const quint32 offset = getWord(dynamicData, m_elfData);
406 if (m_elfData.elfclass == Elf_ELFCLASS64)
407 dynamicData += sizeof(quint32); // past d_ptr.
408 const char *name = mapper.start + dynStrOffset + offset;
409 result.push_back(name);
410 } else {
411 dynamicData += m_elfData.elfclass == Elf_ELFCLASS64 ? 8 : 4;
412 }
413 }
414 return result;
415}
416
ElfType elftype
Definition elfreader.h:117
DebugSymbolsType symbolsType
Definition elfreader.h:123
ElfMachine elfmachine
Definition elfreader.h:118
ElfClass elfclass
Definition elfreader.h:119
QList< ElfProgramHeader > programHeaders
Definition elfreader.h:125
QList< ElfSectionHeader > sectionHeaders
Definition elfreader.h:124
quint64 entryPoint
Definition elfreader.h:120
int indexOf(const QByteArray &name) const
QByteArray debugLink
Definition elfreader.h:121
QByteArray buildId
Definition elfreader.h:122
ElfEndian endian
Definition elfreader.h:116
const char * start
ElfMapper(const ElfReader *reader)
Definition elfreader.cpp:84
QByteArray raw
bool map()
Definition elfreader.cpp:86
quint64 fdlen
const uchar * ustart
QList< QByteArray > dependencies()
ElfReader(const QString &binary)
QByteArray readSection(const QByteArray &sectionName)
QByteArray readCoreName(bool *isCore)
ElfData readHeaders()
QByteArray name
Definition elfreader.h:90
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
QByteArray toHex(char separator='\0') const
Returns a hex encoded copy of the byte array.
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
uchar * map(qint64 offset, qint64 size, MemoryMapFlags flags=NoOptions)
Maps size bytes of the file into memory starting at offset.
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
qint64 size() const override
\reimp
Definition qfile.cpp:1156
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
void append(parameter_type t)
Definition qlist.h:441
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString msgInvalidElfObject(const QString &binary, const QString &why)
quint64 getOffset(const unsigned char *&s, const ElfData &context)
Definition elfreader.cpp:56
quint64 getAddress(const unsigned char *&s, const ElfData &context)
Definition elfreader.cpp:37
quint32 getWord(const unsigned char *&s, const ElfData &context)
Definition elfreader.cpp:26
static QByteArray cutout(const char *s)
DynamicSectionTags
@ DT_STRTAB
@ DT_NEEDED
@ DT_RPATH
@ DT_NULL
@ DT_SONAME
static void parseProgramHeader(const uchar *s, ElfProgramHeader *sh, const ElfData &context)
Definition elfreader.cpp:71
quint16 getHalfWord(const unsigned char *&s, const ElfData &context)
Definition elfreader.cpp:15
static void parseSectionHeader(const uchar *s, ElfSectionHeader *sh, const ElfData &context)
Definition elfreader.cpp:61
@ Elf_PT_NOTE
Definition elfreader.h:19
@ BuildIdSymbols
Definition elfreader.h:82
@ FastSymbols
Definition elfreader.h:84
@ LinkedSymbols
Definition elfreader.h:81
@ PlainSymbols
Definition elfreader.h:83
ElfEndian
Definition elfreader.h:48
@ Elf_ELFDATA2MSB
Definition elfreader.h:51
@ Elf_ELFDATA2LSB
Definition elfreader.h:50
ElfClass
Definition elfreader.h:56
@ Elf_ELFCLASS32
Definition elfreader.h:57
@ Elf_ELFCLASS64
Definition elfreader.h:58
@ Elf_SHT_NOTE
Definition elfreader.h:35
ElfType
Definition elfreader.h:62
@ Elf_ET_CORE
Definition elfreader.h:67
ElfMachine
Definition elfreader.h:71
Combined button and popup list for selecting options.
static void * context
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
AudioChannelLayoutTag tag
static QString header(const QString &name)
GLsizei GLsizei GLenum void * binary
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLenum GLuint GLintptr offset
GLuint name
GLfloat n
GLuint res
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
#define QStringLiteral(str)
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
unsigned short quint16
Definition qtypes.h:43
unsigned long long quint64
Definition qtypes.h:56
long long qint64
Definition qtypes.h:55
QDataWidgetMapper * mapper
[0]