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 <algorithm>
17#include <new>
18
19#include <AutoDeleter.h>
20
21#include "ElfSymbolLookup.h"
22#include "Tracing.h"
23
24
25// #pragma mark - ElfSection
26
27
28ElfSection::ElfSection(const char* name, uint32 type, int fd, uint64 offset,
29	uint64 size, target_addr_t loadAddress, uint32 flags, uint32 linkIndex)
30	:
31	fName(name),
32	fType(type),
33	fFD(fd),
34	fOffset(offset),
35	fSize(size),
36	fData(NULL),
37	fLoadAddress(loadAddress),
38	fFlags(flags),
39	fLoadCount(0),
40	fLinkIndex(linkIndex)
41{
42}
43
44
45ElfSection::~ElfSection()
46{
47	free(fData);
48}
49
50
51status_t
52ElfSection::Load()
53{
54	if (fLoadCount > 0) {
55		fLoadCount++;
56		return B_OK;
57	}
58
59	fData = malloc(fSize);
60	if (fData == NULL)
61		return B_NO_MEMORY;
62
63	ssize_t bytesRead = pread(fFD, fData, fSize, fOffset);
64	if (bytesRead < 0 || (uint64)bytesRead != fSize) {
65		free(fData);
66		fData = NULL;
67		return bytesRead < 0 ? errno : B_ERROR;
68	}
69
70	fLoadCount++;
71	return B_OK;
72}
73
74
75void
76ElfSection::Unload()
77{
78	if (fLoadCount == 0)
79		return;
80
81	if (--fLoadCount == 0) {
82		free(fData);
83		fData = NULL;
84	}
85}
86
87
88// #pragma mark - ElfSegment
89
90
91ElfSegment::ElfSegment(uint32 type, uint64 fileOffset, uint64 fileSize,
92	target_addr_t loadAddress, target_size_t loadSize, uint32 flags)
93	:
94	fFileOffset(fileOffset),
95	fFileSize(fileSize),
96	fLoadAddress(loadAddress),
97	fLoadSize(loadSize),
98	fType(type),
99	fFlags(flags)
100{
101}
102
103
104ElfSegment::~ElfSegment()
105{
106}
107
108
109// #pragma mark - SymbolLookupSource
110
111
112struct ElfFile::SymbolLookupSource : public ElfSymbolLookupSource {
113	SymbolLookupSource(int fd)
114		:
115		fFd(fd),
116		fSegments(8, true)
117	{
118	}
119
120	bool AddSegment(uint64 fileOffset, uint64 fileLength, uint64 memoryAddress)
121	{
122		Segment* segment = new(std::nothrow) Segment(fileOffset, fileLength,
123			memoryAddress);
124		if (segment == NULL || !fSegments.AddItem(segment)) {
125			delete segment;
126			return false;
127		}
128		return true;
129	}
130
131	virtual ssize_t Read(uint64 address, void* buffer, size_t size)
132	{
133		for (int32 i = 0; Segment* segment = fSegments.ItemAt(i); i++) {
134			if (address < segment->fMemoryAddress
135					|| address - segment->fMemoryAddress
136						> segment->fFileLength) {
137				continue;
138			}
139
140			uint64 offset = address - segment->fMemoryAddress;
141			size_t toRead = (size_t)std::min((uint64)size,
142				segment->fFileLength - offset);
143			if (toRead == 0)
144				return 0;
145
146			ssize_t bytesRead = pread(fFd, buffer, toRead,
147				(off_t)(segment->fFileOffset + offset));
148			if (bytesRead < 0)
149				return errno;
150			return bytesRead;
151		}
152
153		return B_BAD_VALUE;
154	}
155
156private:
157	struct Segment {
158		uint64	fFileOffset;
159		uint64	fFileLength;
160		uint64	fMemoryAddress;
161
162		Segment(uint64 fileOffset, uint64 fileLength, uint64 memoryAddress)
163			:
164			fFileOffset(fileOffset),
165			fFileLength(fileLength),
166			fMemoryAddress(memoryAddress)
167		{
168		}
169	};
170
171private:
172	int						fFd;
173	BObjectList<Segment>	fSegments;
174};
175
176
177// #pragma mark - ElfFile
178
179
180ElfFile::ElfFile()
181	:
182	fFileSize(0),
183	fFD(-1),
184	fType(ET_NONE),
185	fMachine(EM_NONE),
186	f64Bit(false),
187	fSwappedByteOrder(false),
188	fSections(16, true),
189	fSegments(16, true)
190{
191}
192
193
194ElfFile::~ElfFile()
195{
196	if (fFD >= 0)
197		close(fFD);
198}
199
200
201status_t
202ElfFile::Init(const char* fileName)
203{
204	// open file
205	fFD = open(fileName, O_RDONLY);
206	if (fFD < 0) {
207		WARNING("Failed to open \"%s\": %s\n", fileName, strerror(errno));
208		return errno;
209	}
210
211	// stat() file to get its size
212	struct stat st;
213	if (fstat(fFD, &st) < 0) {
214		WARNING("Failed to stat \"%s\": %s\n", fileName, strerror(errno));
215		return errno;
216	}
217	fFileSize = st.st_size;
218
219	// Read the identification information to determine whether this is an
220	// ELF file at all and some relevant properties for reading it.
221	uint8 elfIdent[EI_NIDENT];
222	ssize_t bytesRead = pread(fFD, elfIdent, sizeof(elfIdent), 0);
223	if (bytesRead != (ssize_t)sizeof(elfIdent))
224		return bytesRead < 0 ? errno : B_ERROR;
225
226	// magic
227	if (!(memcmp(elfIdent, ELFMAG, 4) == 0))
228		return B_ERROR;
229
230	// endianess
231	if (elfIdent[EI_DATA] == ELFDATA2LSB) {
232		fSwappedByteOrder = B_HOST_IS_BENDIAN != 0;
233	} else if (elfIdent[EI_DATA] == ELFDATA2MSB) {
234		fSwappedByteOrder = B_HOST_IS_LENDIAN != 0;
235	} else {
236		WARNING("%s: Invalid ELF data byte order: %d\n", fileName,
237			elfIdent[EI_DATA]);
238		return B_BAD_DATA;
239	}
240
241	// determine class and load
242	if(elfIdent[EI_CLASS] == ELFCLASS64) {
243		f64Bit = true;
244		return _LoadFile<ElfClass64>(fileName);
245	}
246	if(elfIdent[EI_CLASS] == ELFCLASS32) {
247		f64Bit = false;
248		return _LoadFile<ElfClass32>(fileName);
249	}
250
251	WARNING("%s: Invalid ELF class: %d\n", fileName, elfIdent[EI_CLASS]);
252	return B_BAD_DATA;
253}
254
255
256ElfSection*
257ElfFile::GetSection(const char* name)
258{
259	ElfSection* section = FindSection(name);
260	if (section != NULL && section->Load() == B_OK)
261		return section;
262
263	return NULL;
264}
265
266
267void
268ElfFile::PutSection(ElfSection* section)
269{
270	if (section != NULL)
271		section->Unload();
272}
273
274
275ElfSection*
276ElfFile::FindSection(const char* name) const
277{
278	int32 count = fSections.CountItems();
279	for (int32 i = 0; i < count; i++) {
280		ElfSection* section = fSections.ItemAt(i);
281		if (strcmp(section->Name(), name) == 0)
282			return section;
283	}
284
285	return NULL;
286}
287
288
289ElfSection*
290ElfFile::FindSection(uint32 type) const
291{
292	int32 count = fSections.CountItems();
293	for (int32 i = 0; i < count; i++) {
294		ElfSection* section = fSections.ItemAt(i);
295		if (section->Type() == type)
296			return section;
297	}
298
299	return NULL;
300}
301
302
303ElfSegment*
304ElfFile::TextSegment() const
305{
306	int32 count = fSegments.CountItems();
307	for (int32 i = 0; i < count; i++) {
308		ElfSegment* segment = fSegments.ItemAt(i);
309		if (segment->Type() == PT_LOAD && !segment->IsWritable())
310			return segment;
311	}
312
313	return NULL;
314}
315
316
317ElfSegment*
318ElfFile::DataSegment() const
319{
320	int32 count = fSegments.CountItems();
321	for (int32 i = 0; i < count; i++) {
322		ElfSegment* segment = fSegments.ItemAt(i);
323		if (segment->Type() == PT_LOAD && segment->IsWritable())
324			return segment;
325	}
326
327	return NULL;
328}
329
330
331ElfSymbolLookupSource*
332ElfFile::CreateSymbolLookupSource(uint64 fileOffset, uint64 fileLength,
333	uint64 memoryAddress) const
334{
335	SymbolLookupSource* source = new(std::nothrow) SymbolLookupSource(fFD);
336	if (source == NULL
337			|| !source->AddSegment(fileOffset, fileLength, memoryAddress)) {
338		delete source;
339		return NULL;
340	}
341
342	return source;
343}
344
345
346status_t
347ElfFile::CreateSymbolLookup(uint64 textDelta, ElfSymbolLookup*& _lookup) const
348{
349	// Get the symbol table + corresponding string section. There may be two
350	// symbol tables: the dynamic and the non-dynamic one. The former contains
351	// only the symbols needed at run-time. The latter, if existing, is likely
352	// more complete. So try to find and use the latter one, falling back to the
353	// former.
354	ElfSection* symbolSection;
355	ElfSection* stringSection;
356	if (!_FindSymbolSections(symbolSection, stringSection, SHT_SYMTAB)
357		&& !_FindSymbolSections(symbolSection, stringSection, SHT_DYNSYM)) {
358		return B_ENTRY_NOT_FOUND;
359	}
360
361	// create a source with a segment for each section
362	SymbolLookupSource* source = new(std::nothrow) SymbolLookupSource(fFD);
363	if (source == NULL)
364		return B_NO_MEMORY;
365	BReference<SymbolLookupSource> sourceReference(source, true);
366
367	if (!source->AddSegment(symbolSection->Offset(), symbolSection->Size(),
368				symbolSection->Offset())
369		|| !source->AddSegment(stringSection->Offset(), stringSection->Size(),
370				stringSection->Offset())) {
371		return B_NO_MEMORY;
372	}
373
374	// create the lookup
375	size_t symbolTableEntrySize = Is64Bit()
376		? sizeof(ElfClass64::Sym) : sizeof(ElfClass32::Sym);
377	uint32 symbolCount = uint32(symbolSection->Size() / symbolTableEntrySize);
378
379	return ElfSymbolLookup::Create(source, symbolSection->Offset(), 0,
380		stringSection->Offset(), symbolCount, symbolTableEntrySize, textDelta,
381		f64Bit, fSwappedByteOrder, true, _lookup);
382}
383
384
385template<typename ElfClass>
386status_t
387ElfFile::_LoadFile(const char* fileName)
388{
389	typedef typename ElfClass::Ehdr Ehdr;
390	typedef typename ElfClass::Phdr Phdr;
391	typedef typename ElfClass::Shdr Shdr;
392
393	// read the elf header
394	Ehdr elfHeader;
395	ssize_t bytesRead = pread(fFD, &elfHeader, sizeof(elfHeader), 0);
396	if (bytesRead != (ssize_t)sizeof(elfHeader))
397		return bytesRead < 0 ? errno : B_ERROR;
398
399	// check the ELF header
400	if (!_CheckRange(0, sizeof(elfHeader))
401		|| !_CheckElfHeader<ElfClass>(elfHeader)) {
402		WARNING("\"%s\": Not a valid ELF file\n", fileName);
403		return B_BAD_DATA;
404	}
405
406	fType = Get(elfHeader.e_type);
407	fMachine = Get(elfHeader.e_machine);
408
409	if (Get(elfHeader.e_shnum) > 0) {
410		// check section header table values
411		uint64 sectionHeadersOffset = Get(elfHeader.e_shoff);
412		size_t sectionHeaderSize = Get(elfHeader.e_shentsize);
413		int sectionCount = Get(elfHeader.e_shnum);
414		size_t sectionHeaderTableSize = sectionHeaderSize * sectionCount;
415		if (!_CheckRange(sectionHeadersOffset, sectionHeaderTableSize)) {
416			WARNING("\"%s\": Invalid ELF header\n", fileName);
417			return B_BAD_DATA;
418		}
419
420		// read the section header table
421		uint8* sectionHeaderTable = (uint8*)malloc(sectionHeaderTableSize);
422		if (sectionHeaderTable == NULL)
423			return B_NO_MEMORY;
424		MemoryDeleter sectionHeaderTableDeleter(sectionHeaderTable);
425
426		bytesRead = pread(fFD, sectionHeaderTable, sectionHeaderTableSize,
427			sectionHeadersOffset);
428		if (bytesRead != (ssize_t)sectionHeaderTableSize)
429			return bytesRead < 0 ? errno : B_ERROR;
430
431		// check and get the section header string section
432		Shdr* stringSectionHeader = (Shdr*)(sectionHeaderTable
433			+ Get(elfHeader.e_shstrndx) * sectionHeaderSize);
434		if (!_CheckRange(Get(stringSectionHeader->sh_offset),
435				Get(stringSectionHeader->sh_size))) {
436			WARNING("\"%s\": Invalid string section header\n", fileName);
437			return B_BAD_DATA;
438		}
439		size_t sectionStringSize = Get(stringSectionHeader->sh_size);
440
441		ElfSection* sectionStringSection = new(std::nothrow) ElfSection(
442			".shstrtab", Get(stringSectionHeader->sh_type),fFD,
443			Get(stringSectionHeader->sh_offset), sectionStringSize,
444			Get(stringSectionHeader->sh_addr),
445			Get(stringSectionHeader->sh_flags),
446			Get(stringSectionHeader->sh_link));
447		if (sectionStringSection == NULL)
448			return B_NO_MEMORY;
449		if (!fSections.AddItem(sectionStringSection)) {
450			delete sectionStringSection;
451			return B_NO_MEMORY;
452		}
453
454		status_t error = sectionStringSection->Load();
455		if (error != B_OK)
456			return error;
457
458		const char* sectionStrings = (const char*)sectionStringSection->Data();
459
460		// read the other sections
461		for (int i = 0; i < sectionCount; i++) {
462			Shdr* sectionHeader = (Shdr*)(sectionHeaderTable + i
463				* sectionHeaderSize);
464			// skip invalid sections and the section header string section
465			const char* name = sectionStrings + Get(sectionHeader->sh_name);
466			if (Get(sectionHeader->sh_name) >= sectionStringSize
467				|| !_CheckRange(Get(sectionHeader->sh_offset),
468					Get(sectionHeader->sh_size))
469				|| i == Get(elfHeader.e_shstrndx)) {
470				continue;
471			}
472
473			// create an ElfSection
474			ElfSection* section = new(std::nothrow) ElfSection(name,
475				Get(sectionHeader->sh_type), fFD, Get(sectionHeader->sh_offset),
476				Get(sectionHeader->sh_size), Get(sectionHeader->sh_addr),
477				Get(sectionHeader->sh_flags), Get(sectionHeader->sh_link));
478			if (section == NULL)
479				return B_NO_MEMORY;
480			if (!fSections.AddItem(section)) {
481				delete section;
482				return B_NO_MEMORY;
483			}
484		}
485	}
486
487	if (Get(elfHeader.e_phnum) > 0) {
488		// check program header table values
489		uint64 programHeadersOffset = Get(elfHeader.e_phoff);
490		size_t programHeaderSize = Get(elfHeader.e_phentsize);
491		int segmentCount = Get(elfHeader.e_phnum);
492		size_t programHeaderTableSize = programHeaderSize * segmentCount;
493		if (!_CheckRange(programHeadersOffset, programHeaderTableSize)) {
494			WARNING("\"%s\": Invalid ELF header\n", fileName);
495			return B_BAD_DATA;
496		}
497
498		// read the program header table
499		uint8* programHeaderTable = (uint8*)malloc(programHeaderTableSize);
500		if (programHeaderTable == NULL)
501			return B_NO_MEMORY;
502		MemoryDeleter programHeaderTableDeleter(programHeaderTable);
503
504		bytesRead = pread(fFD, programHeaderTable, programHeaderTableSize,
505			programHeadersOffset);
506		if (bytesRead != (ssize_t)programHeaderTableSize)
507			return bytesRead < 0 ? errno : B_ERROR;
508
509		// read the program headers and create ElfSegment objects
510		for (int i = 0; i < segmentCount; i++) {
511			Phdr* programHeader = (Phdr*)(programHeaderTable + i
512				* programHeaderSize);
513			// skip invalid program headers
514			if (Get(programHeader->p_filesz) > 0
515				&& !_CheckRange(Get(programHeader->p_offset),
516					Get(programHeader->p_filesz))) {
517				continue;
518			}
519
520			// create an ElfSegment
521			ElfSegment* segment = new(std::nothrow) ElfSegment(
522				Get(programHeader->p_type), Get(programHeader->p_offset),
523				Get(programHeader->p_filesz), Get(programHeader->p_vaddr),
524				Get(programHeader->p_memsz), Get(programHeader->p_flags));
525			if (segment == NULL)
526				return B_NO_MEMORY;
527			if (!fSegments.AddItem(segment)) {
528				delete segment;
529				return B_NO_MEMORY;
530			}
531		}
532	}
533
534	return B_OK;
535}
536
537
538bool
539ElfFile::_FindSymbolSections(ElfSection*& _symbolSection,
540	ElfSection*& _stringSection, uint32 type) const
541{
542	// get the symbol table section
543	ElfSection* symbolSection = FindSection(type);
544	if (symbolSection == NULL)
545		return false;
546
547	// The symbol table section is linked to the corresponding string section.
548	ElfSection* stringSection = SectionAt(symbolSection->LinkIndex());
549	if (stringSection == NULL || stringSection->Type() != SHT_STRTAB)
550		return false;
551
552	_symbolSection = symbolSection;
553	_stringSection = stringSection;
554	return true;
555}
556
557
558bool
559ElfFile::_CheckRange(uint64 offset, uint64 size) const
560{
561	return offset < fFileSize && offset + size <= fFileSize;
562}
563
564
565template<typename ElfClass>
566bool
567ElfFile::_CheckElfHeader(typename ElfClass::Ehdr& elfHeader)
568{
569	if (Get(elfHeader.e_shnum) > 0) {
570		if (Get(elfHeader.e_shoff) == 0
571			|| Get(elfHeader.e_shentsize) < sizeof(typename ElfClass::Shdr)
572			|| Get(elfHeader.e_shstrndx) == SHN_UNDEF
573			|| Get(elfHeader.e_shstrndx) >= Get(elfHeader.e_shnum)) {
574			return false;
575		}
576	}
577
578	if (Get(elfHeader.e_phnum) > 0) {
579		if (Get(elfHeader.e_phoff) == 0
580			|| Get(elfHeader.e_phentsize) < sizeof(typename ElfClass::Phdr)) {
581			return false;
582		}
583	}
584
585	return true;
586}
587