1/*
2 * Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "ElfFile.h"
7
8#include <errno.h>
9#include <fcntl.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/stat.h>
14#include <unistd.h>
15
16#include <new>
17
18#include <AutoDeleter.h>
19
20#include "Tracing.h"
21
22
23// #pragma mark - ElfSection
24
25
26ElfSection::ElfSection(const char* name, int fd, off_t offset, off_t size,
27	target_addr_t loadAddress, uint32 flags)
28	:
29	fName(name),
30	fFD(fd),
31	fOffset(offset),
32	fSize(size),
33	fData(NULL),
34	fLoadAddress(loadAddress),
35	fFlags(flags),
36	fLoadCount(0)
37{
38}
39
40
41ElfSection::~ElfSection()
42{
43	free(fData);
44}
45
46
47status_t
48ElfSection::Load()
49{
50	if (fLoadCount > 0) {
51		fLoadCount++;
52		return B_OK;
53	}
54
55	fData = malloc(fSize);
56	if (fData == NULL)
57		return B_NO_MEMORY;
58
59	ssize_t bytesRead = pread(fFD, fData, fSize, fOffset);
60	if (bytesRead != fSize) {
61		free(fData);
62		fData = NULL;
63		return bytesRead < 0 ? errno : B_ERROR;
64	}
65
66	fLoadCount++;
67	return B_OK;
68}
69
70
71void
72ElfSection::Unload()
73{
74	if (fLoadCount == 0)
75		return;
76
77	if (--fLoadCount == 0) {
78		free(fData);
79		fData = NULL;
80	}
81}
82
83
84// #pragma mark - ElfSegment
85
86
87ElfSegment::ElfSegment(off_t fileOffset, off_t fileSize,
88	target_addr_t loadAddress, target_size_t loadSize, bool writable)
89	:
90	fFileOffset(fileOffset),
91	fFileSize(fileSize),
92	fLoadAddress(loadAddress),
93	fLoadSize(loadSize),
94	fWritable(writable)
95{
96}
97
98
99ElfSegment::~ElfSegment()
100{
101}
102
103
104// #pragma mark - ElfFile
105
106
107ElfFile::ElfFile()
108	:
109	fFileSize(0),
110	fFD(-1)
111{
112}
113
114
115ElfFile::~ElfFile()
116{
117	while (ElfSegment* segment = fSegments.RemoveHead())
118		delete segment;
119
120	while (ElfSection* section = fSections.RemoveHead())
121		delete section;
122
123	if (fFD >= 0)
124		close(fFD);
125}
126
127
128status_t
129ElfFile::Init(const char* fileName)
130{
131	// open file
132	fFD = open(fileName, O_RDONLY);
133	if (fFD < 0) {
134		WARNING("Failed to open \"%s\": %s\n", fileName, strerror(errno));
135		return errno;
136	}
137
138	// stat() file to get its size
139	struct stat st;
140	if (fstat(fFD, &st) < 0) {
141		WARNING("Failed to stat \"%s\": %s\n", fileName, strerror(errno));
142		return errno;
143	}
144	fFileSize = st.st_size;
145
146	// Read the identification information to determine the class.
147	uint8 elfIdent[EI_NIDENT];
148	ssize_t bytesRead = pread(fFD, elfIdent, sizeof(elfIdent), 0);
149	if (bytesRead != (ssize_t)sizeof(elfIdent))
150		return bytesRead < 0 ? errno : B_ERROR;
151
152	if(elfIdent[EI_CLASS] == ELFCLASS64)
153		return _LoadFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(fileName);
154	else
155		return _LoadFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(fileName);
156}
157
158
159ElfSection*
160ElfFile::GetSection(const char* name)
161{
162	ElfSection* section = FindSection(name);
163	if (section != NULL && section->Load() == B_OK)
164		return section;
165
166	return NULL;
167}
168
169
170void
171ElfFile::PutSection(ElfSection* section)
172{
173	if (section != NULL)
174		section->Unload();
175}
176
177
178ElfSection*
179ElfFile::FindSection(const char* name) const
180{
181	for (SectionList::ConstIterator it = fSections.GetIterator();
182			ElfSection* section = it.Next();) {
183		if (strcmp(section->Name(), name) == 0)
184			return section;
185	}
186
187	return NULL;
188}
189
190
191ElfSegment*
192ElfFile::TextSegment() const
193{
194	for (SegmentList::ConstIterator it = fSegments.GetIterator();
195			ElfSegment* segment = it.Next();) {
196		if (!segment->IsWritable())
197			return segment;
198	}
199
200	return NULL;
201}
202
203
204ElfSegment*
205ElfFile::DataSegment() const
206{
207	for (SegmentList::ConstIterator it = fSegments.GetIterator();
208			ElfSegment* segment = it.Next();) {
209		if (segment->IsWritable())
210			return segment;
211	}
212
213	return NULL;
214}
215
216
217template<typename Ehdr, typename Phdr, typename Shdr>
218status_t
219ElfFile::_LoadFile(const char* fileName)
220{
221	Ehdr elfHeader;
222
223	// read the elf header
224	ssize_t bytesRead = pread(fFD, &elfHeader, sizeof(Ehdr), 0);
225	if (bytesRead != (ssize_t)sizeof(Ehdr))
226		return bytesRead < 0 ? errno : B_ERROR;
227
228	// check the ELF header
229	if (!_CheckRange(0, sizeof(Ehdr)) || !_CheckElfHeader(elfHeader)) {
230		WARNING("\"%s\": Not an ELF file\n", fileName);
231		return B_BAD_DATA;
232	}
233
234	// check section header table values
235	off_t sectionHeadersOffset = elfHeader.e_shoff;
236	size_t sectionHeaderSize = elfHeader.e_shentsize;
237	int sectionCount = elfHeader.e_shnum;
238	size_t sectionHeaderTableSize = sectionHeaderSize * sectionCount;
239	if (!_CheckRange(sectionHeadersOffset, sectionHeaderTableSize)) {
240		WARNING("\"%s\": Invalid ELF header\n", fileName);
241		return B_BAD_DATA;
242	}
243
244	// read the section header table
245	uint8* sectionHeaderTable = (uint8*)malloc(sectionHeaderTableSize);
246	if (sectionHeaderTable == NULL)
247		return B_NO_MEMORY;
248	MemoryDeleter sectionHeaderTableDeleter(sectionHeaderTable);
249
250	bytesRead = pread(fFD, sectionHeaderTable, sectionHeaderTableSize,
251		sectionHeadersOffset);
252	if (bytesRead != (ssize_t)sectionHeaderTableSize)
253		return bytesRead < 0 ? errno : B_ERROR;
254
255	// check and get the section header string section
256	Shdr* stringSectionHeader = (Shdr*)(sectionHeaderTable
257		+ elfHeader.e_shstrndx * sectionHeaderSize);
258	if (!_CheckRange(stringSectionHeader->sh_offset,
259			stringSectionHeader->sh_size)) {
260		WARNING("\"%s\": Invalid string section header\n", fileName);
261		return B_BAD_DATA;
262	}
263	size_t sectionStringSize = stringSectionHeader->sh_size;
264
265	ElfSection* sectionStringSection = new(std::nothrow) ElfSection(".shstrtab",
266		fFD, stringSectionHeader->sh_offset, sectionStringSize,
267		stringSectionHeader->sh_addr, stringSectionHeader->sh_flags);
268	if (sectionStringSection == NULL)
269		return B_NO_MEMORY;
270	fSections.Add(sectionStringSection);
271
272	status_t error = sectionStringSection->Load();
273	if (error != B_OK)
274		return error;
275
276	const char* sectionStrings = (const char*)sectionStringSection->Data();
277
278	// read the other sections
279	for (int i = 0; i < sectionCount; i++) {
280		Shdr* sectionHeader = (Shdr*)(sectionHeaderTable + i
281			* sectionHeaderSize);
282		// skip invalid sections and the section header string section
283		const char* name = sectionStrings + sectionHeader->sh_name;
284		if (sectionHeader->sh_name >= sectionStringSize
285			|| !_CheckRange(sectionHeader->sh_offset, sectionHeader->sh_size)
286			|| i == elfHeader.e_shstrndx) {
287			continue;
288		}
289
290		// create an ElfSection
291		ElfSection* section = new(std::nothrow) ElfSection(name, fFD,
292			sectionHeader->sh_offset, sectionHeader->sh_size,
293			sectionHeader->sh_addr, sectionHeader->sh_flags);
294		if (section == NULL)
295			return B_NO_MEMORY;
296		fSections.Add(section);
297	}
298
299	// check program header table values
300	off_t programHeadersOffset = elfHeader.e_phoff;
301	size_t programHeaderSize = elfHeader.e_phentsize;
302	int segmentCount = elfHeader.e_phnum;
303	size_t programHeaderTableSize = programHeaderSize * segmentCount;
304	if (!_CheckRange(programHeadersOffset, programHeaderTableSize)) {
305		WARNING("\"%s\": Invalid ELF header\n", fileName);
306		return B_BAD_DATA;
307	}
308
309	// read the program header table
310	uint8* programHeaderTable = (uint8*)malloc(programHeaderTableSize);
311	if (programHeaderTable == NULL)
312		return B_NO_MEMORY;
313	MemoryDeleter programHeaderTableDeleter(programHeaderTable);
314
315	bytesRead = pread(fFD, programHeaderTable, programHeaderTableSize,
316		programHeadersOffset);
317	if (bytesRead != (ssize_t)programHeaderTableSize)
318		return bytesRead < 0 ? errno : B_ERROR;
319
320	// read the program headers and create ElfSegment objects
321	for (int i = 0; i < segmentCount; i++) {
322		Phdr* programHeader = (Phdr*)(programHeaderTable + i
323			* programHeaderSize);
324		// skip program headers we aren't interested in or that are invalid
325		if (programHeader->p_type != PT_LOAD || programHeader->p_filesz == 0
326			|| programHeader->p_memsz == 0
327			|| !_CheckRange(programHeader->p_offset, programHeader->p_filesz)) {
328			continue;
329		}
330
331		// create an ElfSegment
332		ElfSegment* segment = new(std::nothrow) ElfSegment(
333			programHeader->p_offset, programHeader->p_filesz,
334			programHeader->p_vaddr, programHeader->p_memsz,
335			(programHeader->p_flags & PF_WRITE) != 0);
336		if (segment == NULL)
337			return B_NO_MEMORY;
338		fSegments.Add(segment);
339	}
340
341	return B_OK;
342}
343
344
345bool
346ElfFile::_CheckRange(off_t offset, off_t size) const
347{
348	return offset < fFileSize && offset + size < fFileSize;
349}
350
351
352bool
353ElfFile::_CheckElfHeader(Elf32_Ehdr& elfHeader)
354{
355	return memcmp(elfHeader.e_ident, ELF_MAGIC, 4) == 0
356		&& elfHeader.e_ident[4] == ELFCLASS32
357		&& elfHeader.e_shoff > 0
358		&& elfHeader.e_shnum > 0
359		&& elfHeader.e_shentsize >= sizeof(struct Elf32_Shdr)
360		&& elfHeader.e_shstrndx != SHN_UNDEF
361		&& elfHeader.e_shstrndx < elfHeader.e_shnum
362		&& elfHeader.e_phoff > 0
363		&& elfHeader.e_phnum > 0
364		&& elfHeader.e_phentsize >= sizeof(struct Elf32_Phdr);
365}
366
367
368bool
369ElfFile::_CheckElfHeader(Elf64_Ehdr& elfHeader)
370{
371	return memcmp(elfHeader.e_ident, ELF_MAGIC, 4) == 0
372		&& elfHeader.e_ident[4] == ELFCLASS64
373		&& elfHeader.e_shoff > 0
374		&& elfHeader.e_shnum > 0
375		&& elfHeader.e_shentsize >= sizeof(struct Elf64_Shdr)
376		&& elfHeader.e_shstrndx != SHN_UNDEF
377		&& elfHeader.e_shstrndx < elfHeader.e_shnum
378		&& elfHeader.e_phoff > 0
379		&& elfHeader.e_phnum > 0
380		&& elfHeader.e_phentsize >= sizeof(struct Elf64_Phdr);
381}
382