xref: /haiku/src/kits/debugger/elf/ElfFile.cpp (revision 4d8811742fa447ec05b4993a16a0931bc29aafab)
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 
28 ElfSection::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 
45 ElfSection::~ElfSection()
46 {
47 	free(fData);
48 }
49 
50 
51 status_t
52 ElfSection::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 
75 void
76 ElfSection::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 
91 ElfSegment::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 
104 ElfSegment::~ElfSegment()
105 {
106 }
107 
108 
109 // #pragma mark - SymbolLookupSource
110 
111 
112 struct 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 
156 private:
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 
171 private:
172 	int						fFd;
173 	BObjectList<Segment>	fSegments;
174 };
175 
176 
177 // #pragma mark - ElfFile
178 
179 
180 ElfFile::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 
194 ElfFile::~ElfFile()
195 {
196 	if (fFD >= 0)
197 		close(fFD);
198 }
199 
200 
201 status_t
202 ElfFile::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 
256 ElfSection*
257 ElfFile::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 
267 void
268 ElfFile::PutSection(ElfSection* section)
269 {
270 	if (section != NULL)
271 		section->Unload();
272 }
273 
274 
275 ElfSection*
276 ElfFile::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 
289 ElfSection*
290 ElfFile::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 
303 ElfSegment*
304 ElfFile::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 
317 ElfSegment*
318 ElfFile::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 
331 ElfSymbolLookupSource*
332 ElfFile::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 		return NULL;
339 	}
340 
341 	return source;
342 }
343 
344 
345 status_t
346 ElfFile::CreateSymbolLookup(uint64 textDelta, ElfSymbolLookup*& _lookup) const
347 {
348 	// Get the symbol table + corresponding string section. There may be two
349 	// symbol tables: the dynamic and the non-dynamic one. The former contains
350 	// only the symbols needed at run-time. The latter, if existing, is likely
351 	// more complete. So try to find and use the latter one, falling back to the
352 	// former.
353 	ElfSection* symbolSection;
354 	ElfSection* stringSection;
355 	if (!_FindSymbolSections(symbolSection, stringSection, SHT_SYMTAB)
356 		&& !_FindSymbolSections(symbolSection, stringSection, SHT_DYNSYM)) {
357 		return B_ENTRY_NOT_FOUND;
358 	}
359 
360 	// create a source with a segment for each section
361 	SymbolLookupSource* source = new(std::nothrow) SymbolLookupSource(fFD);
362 	if (source == NULL)
363 		return B_NO_MEMORY;
364 	BReference<SymbolLookupSource> sourceReference(source, true);
365 
366 	if (!source->AddSegment(symbolSection->Offset(), symbolSection->Size(),
367 				symbolSection->Offset())
368 		|| !source->AddSegment(stringSection->Offset(), stringSection->Size(),
369 				stringSection->Offset())) {
370 		return B_NO_MEMORY;
371 	}
372 
373 	// create the lookup
374 	size_t symbolTableEntrySize = Is64Bit()
375 		? sizeof(ElfClass64::Sym) : sizeof(ElfClass32::Sym);
376 	uint32 symbolCount = uint32(symbolSection->Size() / symbolTableEntrySize);
377 
378 	return ElfSymbolLookup::Create(source, symbolSection->Offset(), 0,
379 		stringSection->Offset(), symbolCount, symbolTableEntrySize, textDelta,
380 		f64Bit, fSwappedByteOrder, true, _lookup);
381 }
382 
383 
384 template<typename ElfClass>
385 status_t
386 ElfFile::_LoadFile(const char* fileName)
387 {
388 	typedef typename ElfClass::Ehdr Ehdr;
389 	typedef typename ElfClass::Phdr Phdr;
390 	typedef typename ElfClass::Shdr Shdr;
391 
392 	// read the elf header
393 	Ehdr elfHeader;
394 	ssize_t bytesRead = pread(fFD, &elfHeader, sizeof(elfHeader), 0);
395 	if (bytesRead != (ssize_t)sizeof(elfHeader))
396 		return bytesRead < 0 ? errno : B_ERROR;
397 
398 	// check the ELF header
399 	if (!_CheckRange(0, sizeof(elfHeader))
400 		|| !_CheckElfHeader<ElfClass>(elfHeader)) {
401 		WARNING("\"%s\": Not a valid ELF file\n", fileName);
402 		return B_BAD_DATA;
403 	}
404 
405 	fType = Get(elfHeader.e_type);
406 	fMachine = Get(elfHeader.e_machine);
407 
408 	if (Get(elfHeader.e_shnum) > 0) {
409 		// check section header table values
410 		uint64 sectionHeadersOffset = Get(elfHeader.e_shoff);
411 		size_t sectionHeaderSize = Get(elfHeader.e_shentsize);
412 		int sectionCount = Get(elfHeader.e_shnum);
413 		size_t sectionHeaderTableSize = sectionHeaderSize * sectionCount;
414 		if (!_CheckRange(sectionHeadersOffset, sectionHeaderTableSize)) {
415 			WARNING("\"%s\": Invalid ELF header\n", fileName);
416 			return B_BAD_DATA;
417 		}
418 
419 		// read the section header table
420 		uint8* sectionHeaderTable = (uint8*)malloc(sectionHeaderTableSize);
421 		if (sectionHeaderTable == NULL)
422 			return B_NO_MEMORY;
423 		MemoryDeleter sectionHeaderTableDeleter(sectionHeaderTable);
424 
425 		bytesRead = pread(fFD, sectionHeaderTable, sectionHeaderTableSize,
426 			sectionHeadersOffset);
427 		if (bytesRead != (ssize_t)sectionHeaderTableSize)
428 			return bytesRead < 0 ? errno : B_ERROR;
429 
430 		// check and get the section header string section
431 		Shdr* stringSectionHeader = (Shdr*)(sectionHeaderTable
432 			+ Get(elfHeader.e_shstrndx) * sectionHeaderSize);
433 		if (!_CheckRange(Get(stringSectionHeader->sh_offset),
434 				Get(stringSectionHeader->sh_size))) {
435 			WARNING("\"%s\": Invalid string section header\n", fileName);
436 			return B_BAD_DATA;
437 		}
438 		size_t sectionStringSize = Get(stringSectionHeader->sh_size);
439 
440 		ElfSection* sectionStringSection = new(std::nothrow) ElfSection(
441 			".shstrtab", Get(stringSectionHeader->sh_type),fFD,
442 			Get(stringSectionHeader->sh_offset), sectionStringSize,
443 			Get(stringSectionHeader->sh_addr),
444 			Get(stringSectionHeader->sh_flags),
445 			Get(stringSectionHeader->sh_link));
446 		if (sectionStringSection == NULL)
447 			return B_NO_MEMORY;
448 		if (!fSections.AddItem(sectionStringSection)) {
449 			delete sectionStringSection;
450 			return B_NO_MEMORY;
451 		}
452 
453 		status_t error = sectionStringSection->Load();
454 		if (error != B_OK)
455 			return error;
456 
457 		const char* sectionStrings = (const char*)sectionStringSection->Data();
458 
459 		// read the other sections
460 		for (int i = 0; i < sectionCount; i++) {
461 			Shdr* sectionHeader = (Shdr*)(sectionHeaderTable + i
462 				* sectionHeaderSize);
463 			// skip invalid sections and the section header string section
464 			const char* name = sectionStrings + Get(sectionHeader->sh_name);
465 			if (Get(sectionHeader->sh_name) >= sectionStringSize
466 				|| !_CheckRange(Get(sectionHeader->sh_offset),
467 					Get(sectionHeader->sh_size))
468 				|| i == Get(elfHeader.e_shstrndx)) {
469 				continue;
470 			}
471 
472 			// create an ElfSection
473 			ElfSection* section = new(std::nothrow) ElfSection(name,
474 				Get(sectionHeader->sh_type), fFD, Get(sectionHeader->sh_offset),
475 				Get(sectionHeader->sh_size), Get(sectionHeader->sh_addr),
476 				Get(sectionHeader->sh_flags), Get(sectionHeader->sh_link));
477 			if (section == NULL)
478 				return B_NO_MEMORY;
479 			if (!fSections.AddItem(section)) {
480 				delete section;
481 				return B_NO_MEMORY;
482 			}
483 		}
484 	}
485 
486 	if (Get(elfHeader.e_phnum) > 0) {
487 		// check program header table values
488 		uint64 programHeadersOffset = Get(elfHeader.e_phoff);
489 		size_t programHeaderSize = Get(elfHeader.e_phentsize);
490 		int segmentCount = Get(elfHeader.e_phnum);
491 		size_t programHeaderTableSize = programHeaderSize * segmentCount;
492 		if (!_CheckRange(programHeadersOffset, programHeaderTableSize)) {
493 			WARNING("\"%s\": Invalid ELF header\n", fileName);
494 			return B_BAD_DATA;
495 		}
496 
497 		// read the program header table
498 		uint8* programHeaderTable = (uint8*)malloc(programHeaderTableSize);
499 		if (programHeaderTable == NULL)
500 			return B_NO_MEMORY;
501 		MemoryDeleter programHeaderTableDeleter(programHeaderTable);
502 
503 		bytesRead = pread(fFD, programHeaderTable, programHeaderTableSize,
504 			programHeadersOffset);
505 		if (bytesRead != (ssize_t)programHeaderTableSize)
506 			return bytesRead < 0 ? errno : B_ERROR;
507 
508 		// read the program headers and create ElfSegment objects
509 		for (int i = 0; i < segmentCount; i++) {
510 			Phdr* programHeader = (Phdr*)(programHeaderTable + i
511 				* programHeaderSize);
512 			// skip invalid program headers
513 			if (Get(programHeader->p_filesz) > 0
514 				&& !_CheckRange(Get(programHeader->p_offset),
515 					Get(programHeader->p_filesz))) {
516 				continue;
517 			}
518 
519 			// create an ElfSegment
520 			ElfSegment* segment = new(std::nothrow) ElfSegment(
521 				Get(programHeader->p_type), Get(programHeader->p_offset),
522 				Get(programHeader->p_filesz), Get(programHeader->p_vaddr),
523 				Get(programHeader->p_memsz), Get(programHeader->p_flags));
524 			if (segment == NULL)
525 				return B_NO_MEMORY;
526 			if (!fSegments.AddItem(segment)) {
527 				delete segment;
528 				return B_NO_MEMORY;
529 			}
530 		}
531 	}
532 
533 	return B_OK;
534 }
535 
536 
537 bool
538 ElfFile::_FindSymbolSections(ElfSection*& _symbolSection,
539 	ElfSection*& _stringSection, uint32 type) const
540 {
541 	// get the symbol table section
542 	ElfSection* symbolSection = FindSection(type);
543 	if (symbolSection == NULL)
544 		return false;
545 
546 	// The symbol table section is linked to the corresponding string section.
547 	ElfSection* stringSection = SectionAt(symbolSection->LinkIndex());
548 	if (stringSection == NULL || stringSection->Type() != SHT_STRTAB)
549 		return false;
550 
551 	_symbolSection = symbolSection;
552 	_stringSection = stringSection;
553 	return true;
554 }
555 
556 
557 bool
558 ElfFile::_CheckRange(uint64 offset, uint64 size) const
559 {
560 	return offset < fFileSize && offset + size <= fFileSize;
561 }
562 
563 
564 template<typename ElfClass>
565 bool
566 ElfFile::_CheckElfHeader(typename ElfClass::Ehdr& elfHeader)
567 {
568 	if (Get(elfHeader.e_shnum) > 0) {
569 		if (Get(elfHeader.e_shoff) == 0
570 			|| Get(elfHeader.e_shentsize) < sizeof(typename ElfClass::Shdr)
571 			|| Get(elfHeader.e_shstrndx) == SHN_UNDEF
572 			|| Get(elfHeader.e_shstrndx) >= Get(elfHeader.e_shnum)) {
573 			return false;
574 		}
575 	}
576 
577 	if (Get(elfHeader.e_phnum) > 0) {
578 		if (Get(elfHeader.e_phoff) == 0
579 			|| Get(elfHeader.e_phentsize) < sizeof(typename ElfClass::Phdr)) {
580 			return false;
581 		}
582 	}
583 
584 	return true;
585 }
586