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