1 /* 2 * File: StELFFile.cpp 3 * 4 * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. 5 * See included license file for license details. 6 */ 7 8 #include "StELFFile.h" 9 #include <ios> 10 #include <stdexcept> 11 #include <stdio.h> 12 #include "EndianUtilities.h" 13 14 //! \exception StELFFileException is thrown if there is a problem with the file format. 15 //! 16 StELFFile::StELFFile(std::istream & inStream) 17 : m_stream(inStream) 18 { 19 readFileHeaders(); 20 } 21 22 //! Disposes of the string table data. 23 StELFFile::~StELFFile() 24 { 25 SectionDataMap::iterator it = m_sectionDataCache.begin(); 26 for (; it != m_sectionDataCache.end(); ++it) 27 { 28 SectionDataInfo & info = it->second; 29 if (info.m_data != NULL) 30 { 31 delete [] info.m_data; 32 } 33 } 34 } 35 36 //! \exception StELFFileException is thrown if the file is not an ELF file. 37 //! 38 void StELFFile::readFileHeaders() 39 { 40 // move read head to beginning of stream 41 m_stream.seekg(0, std::ios_base::beg); 42 43 // read ELF header 44 m_stream.read(reinterpret_cast<char *>(&m_header), sizeof(m_header)); 45 if (m_stream.bad()) 46 { 47 throw StELFFileException("could not read file header"); 48 } 49 50 // convert endianness 51 m_header.e_type = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_type); 52 m_header.e_machine = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_machine); 53 m_header.e_version = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_version); 54 m_header.e_entry = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_entry); 55 m_header.e_phoff = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_phoff); 56 m_header.e_shoff = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_shoff); 57 m_header.e_flags = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_flags); 58 m_header.e_ehsize = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_ehsize); 59 m_header.e_phentsize = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_phentsize); 60 m_header.e_phnum = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_phnum); 61 m_header.e_shentsize = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_shentsize); 62 m_header.e_shnum = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_shnum); 63 m_header.e_shstrndx = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_shstrndx); 64 65 // check magic number 66 if (!(m_header.e_ident[EI_MAG0] == ELFMAG0 && m_header.e_ident[EI_MAG1] == ELFMAG1 && m_header.e_ident[EI_MAG2] == ELFMAG2 && m_header.e_ident[EI_MAG3] == ELFMAG3)) 67 { 68 throw StELFFileException("invalid magic number in ELF header"); 69 } 70 71 try 72 { 73 int i; 74 75 // read section headers 76 if (m_header.e_shoff != 0 && m_header.e_shnum > 0) 77 { 78 Elf32_Shdr sectionHeader; 79 for (i=0; i < m_header.e_shnum; ++i) 80 { 81 m_stream.seekg(m_header.e_shoff + m_header.e_shentsize * i, std::ios::beg); 82 m_stream.read(reinterpret_cast<char *>(§ionHeader), sizeof(sectionHeader)); 83 if (m_stream.bad()) 84 { 85 throw StELFFileException("could not read section header"); 86 } 87 88 // convert endianness 89 sectionHeader.sh_name = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_name); 90 sectionHeader.sh_type = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_type); 91 sectionHeader.sh_flags = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_flags); 92 sectionHeader.sh_addr = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_addr); 93 sectionHeader.sh_offset = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_offset); 94 sectionHeader.sh_size = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_size); 95 sectionHeader.sh_link = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_link); 96 sectionHeader.sh_info = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_info); 97 sectionHeader.sh_addralign = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_addralign); 98 sectionHeader.sh_entsize = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_entsize); 99 100 m_sectionHeaders.push_back(sectionHeader); 101 } 102 } 103 104 // read program headers 105 if (m_header.e_phoff != 0 && m_header.e_phnum > 0) 106 { 107 Elf32_Phdr programHeader; 108 for (i=0; i < m_header.e_phnum; ++i) 109 { 110 m_stream.seekg(m_header.e_phoff + m_header.e_phentsize * i, std::ios::beg); 111 m_stream.read(reinterpret_cast<char *>(&programHeader), sizeof(programHeader)); 112 if (m_stream.bad()) 113 { 114 throw StELFFileException("could not read program header"); 115 } 116 117 // convert endianness 118 programHeader.p_type = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_type); 119 programHeader.p_offset = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_type); 120 programHeader.p_vaddr = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_vaddr); 121 programHeader.p_paddr = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_paddr); 122 programHeader.p_filesz = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_filesz); 123 programHeader.p_memsz = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_memsz); 124 programHeader.p_flags = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_flags); 125 programHeader.p_align = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_align); 126 127 m_programHeaders.push_back(programHeader); 128 } 129 } 130 131 // look up symbol table section index 132 { 133 std::string symtab_section_name(SYMTAB_SECTION_NAME); 134 m_symbolTableIndex = getIndexOfSectionWithName(symtab_section_name); 135 } 136 } 137 catch (...) 138 { 139 throw StELFFileException("error reading file"); 140 } 141 } 142 143 const Elf32_Shdr & StELFFile::getSectionAtIndex(unsigned inIndex) const 144 { 145 if (inIndex > m_sectionHeaders.size()) 146 throw std::invalid_argument("inIndex"); 147 148 return m_sectionHeaders[inIndex]; 149 } 150 151 //! If there is not a matching section, then #SHN_UNDEF is returned instead. 152 //! 153 unsigned StELFFile::getIndexOfSectionWithName(const std::string & inName) 154 { 155 unsigned sectionIndex = 0; 156 const_section_iterator it = getSectionBegin(); 157 for (; it != getSectionEnd(); ++it, ++sectionIndex) 158 { 159 const Elf32_Shdr & header = *it; 160 if (header.sh_name != 0) 161 { 162 std::string sectionName = getSectionNameAtIndex(header.sh_name); 163 if (inName == sectionName) 164 return sectionIndex; 165 } 166 } 167 168 // no matching section 169 return SHN_UNDEF; 170 } 171 172 //! The pointer returned from this method must be freed with the delete array operator (i.e., delete []). 173 //! If either the section data offset (sh_offset) or the section size (sh_size) are 0, then NULL will 174 //! be returned instead. 175 //! 176 //! The data is read directly from the input stream passed into the constructor. The stream must 177 //! still be open, or an exception will be thrown. 178 //! 179 //! \exception StELFFileException is thrown if an error occurs while reading the file. 180 //! \exception std::bad_alloc is thrown if memory for the data cannot be allocated. 181 uint8_t * StELFFile::getSectionDataAtIndex(unsigned inIndex) 182 { 183 return readSectionData(m_sectionHeaders[inIndex]); 184 } 185 186 //! The pointer returned from this method must be freed with the delete array operator (i.e., delete []). 187 //! If either the section data offset (sh_offset) or the section size (sh_size) are 0, then NULL will 188 //! be returned instead. 189 //! 190 //! The data is read directly from the input stream passed into the constructor. The stream must 191 //! still be open, or an exception will be thrown. 192 //! 193 //! \exception StELFFileException is thrown if an error occurs while reading the file. 194 //! \exception std::bad_alloc is thrown if memory for the data cannot be allocated. 195 uint8_t * StELFFile::getSectionData(const_section_iterator inSection) 196 { 197 return readSectionData(*inSection); 198 } 199 200 //! \exception StELFFileException is thrown if an error occurs while reading the file. 201 //! \exception std::bad_alloc is thrown if memory for the data cannot be allocated. 202 uint8_t * StELFFile::readSectionData(const Elf32_Shdr & inHeader) 203 { 204 // check for empty data 205 if (inHeader.sh_offset == 0 || inHeader.sh_size == 0) 206 return NULL; 207 208 uint8_t * sectionData = new uint8_t[inHeader.sh_size]; 209 210 try 211 { 212 m_stream.seekg(inHeader.sh_offset, std::ios::beg); 213 m_stream.read(reinterpret_cast<char *>(sectionData), inHeader.sh_size); 214 if (m_stream.bad()) 215 throw StELFFileException("could not read entire section"); 216 } 217 catch (StELFFileException) 218 { 219 throw; 220 } 221 catch (...) 222 { 223 throw StELFFileException("error reading section data"); 224 } 225 226 return sectionData; 227 } 228 229 const Elf32_Phdr & StELFFile::getSegmentAtIndex(unsigned inIndex) const 230 { 231 if (inIndex > m_programHeaders.size()) 232 throw std::invalid_argument("inIndex"); 233 234 return m_programHeaders[inIndex]; 235 } 236 237 //! The pointer returned from this method must be freed with the delete array operator (i.e., delete []). 238 //! If either the segment offset (p_offset) or the segment file size (p_filesz) are 0, then NULL will 239 //! be returned instead. 240 //! 241 //! The data is read directly from the input stream passed into the constructor. The stream must 242 //! still be open, or an exception will be thrown. 243 //! 244 //! \exception StELFFileException is thrown if an error occurs while reading the file. 245 //! \exception std::bad_alloc is thrown if memory for the data cannot be allocated. 246 uint8_t * StELFFile::getSegmentDataAtIndex(unsigned inIndex) 247 { 248 return readSegmentData(m_programHeaders[inIndex]); 249 } 250 251 //! The pointer returned from this method must be freed with the delete array operator (i.e., delete []). 252 //! If either the segment offset (p_offset) or the segment file size (p_filesz) are 0, then NULL will 253 //! be returned instead. 254 //! 255 //! The data is read directly from the input stream passed into the constructor. The stream must 256 //! still be open, or an exception will be thrown. 257 //! 258 //! \exception StELFFileException is thrown if an error occurs while reading the file. 259 //! \exception std::bad_alloc is thrown if memory for the data cannot be allocated. 260 uint8_t * StELFFile::getSegmentData(const_segment_iterator inSegment) 261 { 262 return readSegmentData(*inSegment); 263 } 264 265 //! \exception StELFFileException is thrown if an error occurs while reading the file. 266 //! \exception std::bad_alloc is thrown if memory for the data cannot be allocated. 267 uint8_t * StELFFile::readSegmentData(const Elf32_Phdr & inHeader) 268 { 269 // check for empty data 270 if (inHeader.p_offset == 0 || inHeader.p_filesz== 0) 271 return NULL; 272 273 uint8_t * segmentData = new uint8_t[inHeader.p_filesz]; 274 275 try 276 { 277 m_stream.seekg(inHeader.p_offset, std::ios::beg); 278 m_stream.read(reinterpret_cast<char *>(segmentData), inHeader.p_filesz); 279 if (m_stream.bad()) 280 throw StELFFileException("could not read entire segment"); 281 } 282 catch (StELFFileException) 283 { 284 throw; 285 } 286 catch (...) 287 { 288 throw StELFFileException("error reading segment data"); 289 } 290 291 return segmentData; 292 } 293 294 //! If the index is out of range, or if there is no string table in the file, then 295 //! an empty string will be returned instead. This will also happen when the index 296 //! is either 0 or the last byte in the table, since the table begins and ends with 297 //! zero bytes. 298 std::string StELFFile::getSectionNameAtIndex(unsigned inIndex) 299 { 300 // make sure there's a section name string table 301 if (m_header.e_shstrndx == SHN_UNDEF) 302 return std::string(""); 303 304 return getStringAtIndex(m_header.e_shstrndx, inIndex); 305 } 306 307 //! \exception std::invalid_argument is thrown if the section identified by \a 308 //! inStringTableSectionIndex is not actually a string table, or if \a 309 //! inStringIndex is out of range for the string table. 310 std::string StELFFile::getStringAtIndex(unsigned inStringTableSectionIndex, unsigned inStringIndex) 311 { 312 // check section type 313 const Elf32_Shdr & header = getSectionAtIndex(inStringTableSectionIndex); 314 if (header.sh_type != SHT_STRTAB) 315 throw std::invalid_argument("inStringTableSectionIndex"); 316 317 if (inStringIndex >= header.sh_size) 318 throw std::invalid_argument("inStringTableSectionIndex"); 319 320 // check cache 321 SectionDataInfo & info = getCachedSectionData(inStringTableSectionIndex); 322 return std::string(&reinterpret_cast<char *>(info.m_data)[inStringIndex]); 323 } 324 325 StELFFile::SectionDataInfo & StELFFile::getCachedSectionData(unsigned inSectionIndex) 326 { 327 // check cache 328 SectionDataMap::iterator it = m_sectionDataCache.find(inSectionIndex); 329 if (it != m_sectionDataCache.end()) 330 return it->second; 331 332 // not in cache, add it 333 const Elf32_Shdr & header = getSectionAtIndex(inSectionIndex); 334 uint8_t * data = getSectionDataAtIndex(inSectionIndex); 335 336 SectionDataInfo info; 337 info.m_data = data; 338 info.m_size = header.sh_size; 339 340 m_sectionDataCache[inSectionIndex] = info; 341 return m_sectionDataCache[inSectionIndex]; 342 } 343 344 //! The number of entries in the symbol table is the symbol table section size 345 //! divided by the size of each symbol entry (the #Elf32_Shdr::sh_entsize field of the 346 //! symbol table section header). 347 unsigned StELFFile::getSymbolCount() 348 { 349 if (m_symbolTableIndex == SHN_UNDEF) 350 return 0; 351 352 const Elf32_Shdr & header = getSectionAtIndex(m_symbolTableIndex); 353 return header.sh_size / header.sh_entsize; 354 } 355 356 //! \exception std::invalid_argument is thrown if \a inIndex is out of range.] 357 //! 358 const Elf32_Sym & StELFFile::getSymbolAtIndex(unsigned inIndex) 359 { 360 // get section data 361 const Elf32_Shdr & header = getSectionAtIndex(m_symbolTableIndex); 362 SectionDataInfo & info = getCachedSectionData(m_symbolTableIndex); 363 364 // has the symbol table been byte swapped yet? 365 if (!info.m_swapped) 366 { 367 byteSwapSymbolTable(header, info); 368 } 369 370 unsigned symbolOffset = header.sh_entsize * inIndex; 371 if (symbolOffset >= info.m_size) 372 { 373 throw std::invalid_argument("inIndex"); 374 } 375 376 Elf32_Sym * symbol = reinterpret_cast<Elf32_Sym *>(&info.m_data[symbolOffset]); 377 return *symbol; 378 } 379 380 void StELFFile::byteSwapSymbolTable(const Elf32_Shdr & header, SectionDataInfo & info) 381 { 382 unsigned symbolCount = getSymbolCount(); 383 unsigned i = 0; 384 unsigned symbolOffset = 0; 385 386 for (; i < symbolCount; ++i, symbolOffset += header.sh_entsize) 387 { 388 Elf32_Sym * symbol = reinterpret_cast<Elf32_Sym *>(&info.m_data[symbolOffset]); 389 symbol->st_name = ENDIAN_LITTLE_TO_HOST_U32(symbol->st_name); 390 symbol->st_value = ENDIAN_LITTLE_TO_HOST_U32(symbol->st_value); 391 symbol->st_size = ENDIAN_LITTLE_TO_HOST_U32(symbol->st_size); 392 symbol->st_shndx = ENDIAN_LITTLE_TO_HOST_U16(symbol->st_shndx); 393 } 394 395 // remember that we've byte swapped the symbols 396 info.m_swapped = true; 397 } 398 399 unsigned StELFFile::getSymbolNameStringTableIndex() const 400 { 401 const Elf32_Shdr & header = getSectionAtIndex(m_symbolTableIndex); 402 return header.sh_link; 403 } 404 405 std::string StELFFile::getSymbolName(const Elf32_Sym & inSymbol) 406 { 407 unsigned symbolStringTableIndex = getSymbolNameStringTableIndex(); 408 return getStringAtIndex(symbolStringTableIndex, inSymbol.st_name); 409 } 410 411 //! Returns STN_UNDEF if it cannot find a symbol at the given \a symbolAddress. 412 unsigned StELFFile::getIndexOfSymbolAtAddress(uint32_t symbolAddress, bool strict) 413 { 414 unsigned symbolCount = getSymbolCount(); 415 unsigned symbolIndex = 0; 416 for (; symbolIndex < symbolCount; ++symbolIndex) 417 { 418 const Elf32_Sym & symbol = getSymbolAtIndex(symbolIndex); 419 420 // the GHS toolchain puts in STT_FUNC symbols marking the beginning and ending of each 421 // file. if the entry point happens to be at the beginning of the file, the beginning- 422 // of-file symbol will have the same value and type. fortunately, the size of these 423 // symbols is 0 (or seems to be). we also ignore symbols that start with two dots just 424 // in case. 425 if (symbol.st_value == symbolAddress && (strict && ELF32_ST_TYPE(symbol.st_info) == STT_FUNC && symbol.st_size != 0)) 426 { 427 std::string symbolName = getSymbolName(symbol); 428 429 // ignore symbols that start with two dots 430 if (symbolName[0] == '.' && symbolName[1] == '.') 431 continue; 432 433 // found the symbol! 434 return symbolIndex; 435 } 436 } 437 438 return STN_UNDEF; 439 } 440 441 ARMSymbolType_t StELFFile::getTypeOfSymbolAtIndex(unsigned symbolIndex) 442 { 443 ARMSymbolType_t symType = eARMSymbol; 444 const Elf32_Sym & symbol = getSymbolAtIndex(symbolIndex); 445 446 if (m_elfVariant == eGHSVariant) 447 { 448 if (symbol.st_other & STO_THUMB) 449 symType = eThumbSymbol; 450 } 451 else 452 { 453 unsigned mappingSymStart = 1; 454 unsigned mappingSymCount = getSymbolCount() - 1; // don't include first undefined symbol 455 bool mapSymsFirst = (m_header.e_flags & EF_ARM_MAPSYMSFIRST) != 0; 456 if (mapSymsFirst) 457 { 458 // first symbol '$m' is number of mapping syms 459 const Elf32_Sym & mappingSymCountSym = getSymbolAtIndex(1); 460 if (getSymbolName(mappingSymCountSym) == MAPPING_SYMBOL_COUNT_TAGSYM) 461 { 462 mappingSymCount = mappingSymCountSym.st_value; 463 mappingSymStart = 2; 464 } 465 466 } 467 468 uint32_t lastMappingSymAddress = 0; 469 unsigned mappingSymIndex = mappingSymStart; 470 for (; mappingSymIndex < mappingSymCount + mappingSymStart; ++mappingSymIndex) 471 { 472 const Elf32_Sym & mappingSym = getSymbolAtIndex(mappingSymIndex); 473 std::string mappingSymName = getSymbolName(mappingSym); 474 ARMSymbolType_t nextSymType = eUnknownSymbol; 475 476 if (mappingSymName == ARM_SEQUENCE_MAPSYM) 477 symType = eARMSymbol; 478 else if (mappingSymName == DATA_SEQUENCE_MAPSYM) 479 symType = eDataSymbol; 480 else if (mappingSymName == THUMB_SEQUENCE_MAPSYM) 481 symType = eThumbSymbol; 482 483 if (nextSymType != eUnknownSymbol) 484 { 485 if (symbol.st_value >= lastMappingSymAddress && symbol.st_value < mappingSym.st_value) 486 break; 487 488 symType = nextSymType; 489 lastMappingSymAddress = mappingSym.st_value; 490 } 491 } 492 } 493 494 return symType; 495 } 496 497 void StELFFile::dumpSections() 498 { 499 unsigned count = getSectionCount(); 500 unsigned i = 0; 501 502 const char * sectionTypes[12] = { "NULL", "PROGBITS", "SYMTAB", "STRTAB", "RELA", "HASH", "DYNAMIC", "NOTE", "NOBITS", "REL", "SHLIB", "DYNSYM" }; 503 504 for (; i < count; ++i) 505 { 506 const Elf32_Shdr & header = getSectionAtIndex(i); 507 std::string name = getSectionNameAtIndex(header.sh_name); 508 509 printf("%s: %s, 0x%08x, 0x%08x, 0x%08x, %d, %d, %d\n", name.c_str(), sectionTypes[header.sh_type], header.sh_addr, header.sh_offset, header.sh_size, header.sh_link, header.sh_info, header.sh_entsize); 510 } 511 } 512 513 void StELFFile::dumpSymbolTable() 514 { 515 const char * symbolTypes[5] = { "NOTYPE", "OBJECT", "FUNC", "SECTION", "FILE" }; 516 const char * symbolBinding[3] = { "LOCAL", "GLOBAL", "WEAK" }; 517 518 unsigned count = getSymbolCount(); 519 unsigned i = 0; 520 521 for (; i < count; ++i) 522 { 523 const Elf32_Sym & symbol = getSymbolAtIndex(i); 524 std::string name = getSymbolName(symbol); 525 526 printf("'%s': %s, %s, 0x%08x, 0x%08x, %d. 0x%08x\n", name.c_str(), symbolTypes[ELF32_ST_TYPE(symbol.st_info)], symbolBinding[ELF32_ST_BIND(symbol.st_info)], symbol.st_value, symbol.st_size, symbol.st_shndx, symbol.st_other); 527 } 528 } 529 530 531 532