Debugger: Add dedicated functions for global {un}init.
[haiku.git] / src / apps / debugger / elf / ElfFile.cpp
blob72f2b98b8772bdc5e0e4bf4a48fd9127e786686a
1 /*
2 * Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include "ElfFile.h"
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>
16 #include <algorithm>
17 #include <new>
19 #include <AutoDeleter.h>
21 #include "ElfSymbolLookup.h"
22 #include "Tracing.h"
25 // #pragma mark - ElfSection
28 ElfSection::ElfSection(const char* name, uint32 type, int fd, uint64 offset,
29 uint64 size, target_addr_t loadAddress, uint32 flags, uint32 linkIndex)
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)
45 ElfSection::~ElfSection()
47 free(fData);
51 status_t
52 ElfSection::Load()
54 if (fLoadCount > 0) {
55 fLoadCount++;
56 return B_OK;
59 fData = malloc(fSize);
60 if (fData == NULL)
61 return B_NO_MEMORY;
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;
70 fLoadCount++;
71 return B_OK;
75 void
76 ElfSection::Unload()
78 if (fLoadCount == 0)
79 return;
81 if (--fLoadCount == 0) {
82 free(fData);
83 fData = NULL;
88 // #pragma mark - ElfSegment
91 ElfSegment::ElfSegment(uint32 type, uint64 fileOffset, uint64 fileSize,
92 target_addr_t loadAddress, target_size_t loadSize, uint32 flags)
94 fFileOffset(fileOffset),
95 fFileSize(fileSize),
96 fLoadAddress(loadAddress),
97 fLoadSize(loadSize),
98 fType(type),
99 fFlags(flags)
104 ElfSegment::~ElfSegment()
109 // #pragma mark - SymbolLookupSource
112 struct ElfFile::SymbolLookupSource : public ElfSymbolLookupSource {
113 SymbolLookupSource(int fd)
115 fFd(fd),
116 fSegments(8, true)
120 bool AddSegment(uint64 fileOffset, uint64 fileLength, uint64 memoryAddress)
122 Segment* segment = new(std::nothrow) Segment(fileOffset, fileLength,
123 memoryAddress);
124 if (segment == NULL || !fSegments.AddItem(segment)) {
125 delete segment;
126 return false;
128 return true;
131 virtual ssize_t Read(uint64 address, void* buffer, size_t size)
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;
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;
146 ssize_t bytesRead = pread(fFd, buffer, toRead,
147 (off_t)(segment->fFileOffset + offset));
148 if (bytesRead < 0)
149 return errno;
150 return bytesRead;
153 return B_BAD_VALUE;
156 private:
157 struct Segment {
158 uint64 fFileOffset;
159 uint64 fFileLength;
160 uint64 fMemoryAddress;
162 Segment(uint64 fileOffset, uint64 fileLength, uint64 memoryAddress)
164 fFileOffset(fileOffset),
165 fFileLength(fileLength),
166 fMemoryAddress(memoryAddress)
171 private:
172 int fFd;
173 BObjectList<Segment> fSegments;
177 // #pragma mark - ElfFile
180 ElfFile::ElfFile()
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)
194 ElfFile::~ElfFile()
196 if (fFD >= 0)
197 close(fFD);
201 status_t
202 ElfFile::Init(const char* fileName)
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;
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;
217 fFileSize = st.st_size;
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;
226 // magic
227 if (!memcmp(elfIdent, ELF_MAGIC, 4) == 0)
228 return B_ERROR;
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;
241 // determine class and load
242 if(elfIdent[EI_CLASS] == ELFCLASS64) {
243 f64Bit = true;
244 return _LoadFile<ElfClass64>(fileName);
246 if(elfIdent[EI_CLASS] == ELFCLASS32) {
247 f64Bit = false;
248 return _LoadFile<ElfClass32>(fileName);
251 WARNING("%s: Invalid ELF class: %d\n", fileName, elfIdent[EI_CLASS]);
252 return B_BAD_DATA;
256 ElfSection*
257 ElfFile::GetSection(const char* name)
259 ElfSection* section = FindSection(name);
260 if (section != NULL && section->Load() == B_OK)
261 return section;
263 return NULL;
267 void
268 ElfFile::PutSection(ElfSection* section)
270 if (section != NULL)
271 section->Unload();
275 ElfSection*
276 ElfFile::FindSection(const char* name) const
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;
285 return NULL;
289 ElfSection*
290 ElfFile::FindSection(uint32 type) const
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;
299 return NULL;
303 ElfSegment*
304 ElfFile::TextSegment() const
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;
313 return NULL;
317 ElfSegment*
318 ElfFile::DataSegment() const
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;
327 return NULL;
331 ElfSymbolLookupSource*
332 ElfFile::CreateSymbolLookupSource(uint64 fileOffset, uint64 fileLength,
333 uint64 memoryAddress) const
335 SymbolLookupSource* source = new(std::nothrow) SymbolLookupSource(fFD);
336 if (source == NULL
337 || !source->AddSegment(fileOffset, fileLength, memoryAddress)) {
338 return NULL;
341 return source;
345 status_t
346 ElfFile::CreateSymbolLookup(uint64 textDelta, ElfSymbolLookup*& _lookup) const
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;
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);
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;
373 // create the lookup
374 size_t symbolTableEntrySize = Is64Bit()
375 ? sizeof(ElfClass64::Sym) : sizeof(ElfClass32::Sym);
376 uint32 symbolCount = uint32(symbolSection->Size() / symbolTableEntrySize);
378 return ElfSymbolLookup::Create(source, symbolSection->Offset(), 0,
379 stringSection->Offset(), symbolCount, symbolTableEntrySize, textDelta,
380 f64Bit, fSwappedByteOrder, true, _lookup);
384 template<typename ElfClass>
385 status_t
386 ElfFile::_LoadFile(const char* fileName)
388 typedef typename ElfClass::Ehdr Ehdr;
389 typedef typename ElfClass::Phdr Phdr;
390 typedef typename ElfClass::Shdr Shdr;
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;
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;
405 fType = Get(elfHeader.e_type);
406 fMachine = Get(elfHeader.e_machine);
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;
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);
425 bytesRead = pread(fFD, sectionHeaderTable, sectionHeaderTableSize,
426 sectionHeadersOffset);
427 if (bytesRead != (ssize_t)sectionHeaderTableSize)
428 return bytesRead < 0 ? errno : B_ERROR;
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;
438 size_t sectionStringSize = Get(stringSectionHeader->sh_size);
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;
453 status_t error = sectionStringSection->Load();
454 if (error != B_OK)
455 return error;
457 const char* sectionStrings = (const char*)sectionStringSection->Data();
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;
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, i)) {
480 delete section;
481 return B_NO_MEMORY;
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;
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);
503 bytesRead = pread(fFD, programHeaderTable, programHeaderTableSize,
504 programHeadersOffset);
505 if (bytesRead != (ssize_t)programHeaderTableSize)
506 return bytesRead < 0 ? errno : B_ERROR;
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;
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;
533 return B_OK;
537 bool
538 ElfFile::_FindSymbolSections(ElfSection*& _symbolSection,
539 ElfSection*& _stringSection, uint32 type) const
541 // get the symbol table section
542 ElfSection* symbolSection = FindSection(type);
543 if (symbolSection == NULL)
544 return false;
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;
551 _symbolSection = symbolSection;
552 _stringSection = stringSection;
553 return true;
557 bool
558 ElfFile::_CheckRange(uint64 offset, uint64 size) const
560 return offset < fFileSize && offset + size <= fFileSize;
564 template<typename ElfClass>
565 bool
566 ElfFile::_CheckElfHeader(typename ElfClass::Ehdr& elfHeader)
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;
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;
584 return true;