1 // ResourceFile.cpp 2 3 #include "ResourceFile.h" 4 5 #include <algobase.h> 6 #include <stdio.h> 7 8 #include "Elf.h" 9 #include "Exception.h" 10 #include "Pef.h" 11 #include "ResourceItem.h" 12 #include "ResourcesDefs.h" 13 #include "Warnings.h" 14 15 // ELF defs 16 static const uint32 kMaxELFHeaderSize = sizeof(Elf32_Ehdr) + 32; 17 static const char kELFFileMagic[4] = { 0x7f, 'E', 'L', 'F' }; 18 19 // sanity bounds 20 static const uint32 kMaxResourceCount = 10000; 21 static const uint32 kELFMaxResourceAlignment = 1024 * 1024 * 10; // 10 MB 22 23 // recognized file types (indices into kFileTypeNames) 24 enum { 25 FILE_TYPE_UNKNOWN = 0, 26 FILE_TYPE_X86_RESOURCE = 1, 27 FILE_TYPE_PPC_RESOURCE = 2, 28 FILE_TYPE_ELF = 3, 29 FILE_TYPE_PEF = 4, 30 }; 31 32 const char* kFileTypeNames[] = { 33 "unknown", 34 "x86 resource file", 35 "PPC resource file", 36 "ELF object file", 37 "PEF object file", 38 }; 39 40 41 // helper functions/classes 42 43 // read_exactly 44 static 45 void 46 read_exactly(BPositionIO& file, off_t position, void* buffer, size_t size, 47 const char* errorMessage = NULL) 48 { 49 ssize_t read = file.ReadAt(position, buffer, size); 50 if (read < 0) 51 throw Exception(read, errorMessage); 52 else if ((size_t)read != size) { 53 if (errorMessage) { 54 throw Exception("%s Read to few bytes (%ld/%lu).", errorMessage, 55 read, size); 56 } else 57 throw Exception("Read to few bytes (%ld/%lu).", read, size); 58 } 59 } 60 61 // align_value 62 template<typename TV, typename TA> 63 static inline 64 TV 65 align_value(const TV& value, const TA& alignment) 66 { 67 return ((value + alignment - 1) / alignment) * alignment; 68 } 69 70 // calculate_checksum 71 static 72 uint32 73 calculate_checksum(const void* data, uint32 size) 74 { 75 uint32 checkSum = 0; 76 const uint8* csData = (const uint8*)data; 77 const uint8* dataEnd = csData + size; 78 const uint8* current = csData; 79 for (; current < dataEnd; current += 4) { 80 uint32 word = 0; 81 int32 bytes = min(4L, dataEnd - current); 82 for (int32 i = 0; i < bytes; i++) 83 word = (word << 8) + current[i]; 84 checkSum += word; 85 } 86 return checkSum; 87 } 88 89 // skip_bytes 90 static inline 91 const void* 92 skip_bytes(const void* buffer, int32 offset) 93 { 94 return (const char*)buffer + offset; 95 } 96 97 // skip_bytes 98 static inline 99 void* 100 skip_bytes(void* buffer, int32 offset) 101 { 102 return (char*)buffer + offset; 103 } 104 105 // fill_pattern 106 static 107 void 108 fill_pattern(uint32 byteOffset, void* _buffer, uint32 count) 109 { 110 uint32* buffer = (uint32*)_buffer; 111 for (uint32 i = 0; i < count; i++) 112 buffer[i] = kUnusedResourceDataPattern[(byteOffset / 4 + i) % 3]; 113 } 114 115 // fill_pattern 116 static 117 void 118 fill_pattern(const void* dataBegin, void* buffer, uint32 count) 119 { 120 fill_pattern((char*)buffer - (const char*)dataBegin, buffer, count); 121 } 122 123 // fill_pattern 124 static 125 void 126 fill_pattern(const void* dataBegin, void* buffer, const void* bufferEnd) 127 { 128 fill_pattern(dataBegin, buffer, 129 ((const char*)bufferEnd - (char*)buffer) / 4); 130 } 131 132 // check_pattern 133 static 134 bool 135 check_pattern(uint32 byteOffset, void* _buffer, uint32 count, 136 bool hostEndianess) 137 { 138 bool result = true; 139 uint32* buffer = (uint32*)_buffer; 140 for (uint32 i = 0; result && i < count; i++) { 141 uint32 value = buffer[i]; 142 if (!hostEndianess) 143 value = B_SWAP_INT32(value); 144 result 145 = (value == kUnusedResourceDataPattern[(byteOffset / 4 + i) % 3]); 146 } 147 return result; 148 } 149 150 // MemArea 151 struct MemArea { 152 MemArea(const void* data, uint32 size) : data(data), size(size) {} 153 154 inline bool check(const void* _current, uint32 skip = 0) const 155 { 156 const char* start = (const char*)data; 157 const char* current = (const char*)_current; 158 return (start <= current && start + size >= current + skip); 159 } 160 161 const void* data; 162 uint32 size; 163 }; 164 165 // AutoDeleter 166 template<typename C> 167 struct AutoDeleter { 168 AutoDeleter(C* object, bool array = false) : object(object), array(array) 169 { 170 } 171 172 ~AutoDeleter() 173 { 174 if (array) 175 delete[] object; 176 else 177 delete object; 178 } 179 180 C* object; 181 bool array; 182 }; 183 184 185 // constructor 186 ResourceFile::ResourceFile() 187 : fItems(), 188 fFile(), 189 fFileType(FILE_TYPE_UNKNOWN), 190 fFileSize(0), 191 fResourceCount(0), 192 fInfoTableItem(NULL), 193 fHostEndianess(true) 194 { 195 } 196 197 // destructor 198 ResourceFile::~ResourceFile() 199 { 200 Unset(); 201 } 202 203 // Init 204 void 205 ResourceFile::Init(BFile& file) 206 { 207 Unset(); 208 try { 209 _InitFile(file); 210 _ReadHeader(); 211 _ReadIndex(); 212 _ReadInfoTable(); 213 } catch (Exception exception) { 214 Unset(); 215 throw exception; 216 } 217 } 218 219 // Unset 220 void 221 ResourceFile::Unset() 222 { 223 // items 224 for (int32 i = 0; ResourceItem* item = ItemAt(i); i++) 225 delete item; 226 fItems.MakeEmpty(); 227 // file 228 fFile.Unset(); 229 fFileType = FILE_TYPE_UNKNOWN; 230 fFileSize = 0; 231 // resource count 232 fResourceCount = 0; 233 // info table 234 delete fInfoTableItem; 235 fInfoTableItem = NULL; 236 fHostEndianess = true; 237 } 238 239 // InitCheck 240 status_t 241 ResourceFile::InitCheck() const 242 { 243 return fFile.InitCheck(); 244 } 245 246 // AddItem 247 bool 248 ResourceFile::AddItem(ResourceItem* item, int32 index) 249 { 250 bool result = false; 251 if (item) { 252 if (index < 0 || index > CountItems()) 253 index = CountItems(); 254 result = fItems.AddItem(item); 255 } 256 return result; 257 } 258 259 // RemoveItem 260 ResourceItem* 261 ResourceFile::RemoveItem(int32 index) 262 { 263 return (ResourceItem*)fItems.RemoveItem(index); 264 } 265 266 // RemoveItem 267 bool 268 ResourceFile::RemoveItem(ResourceItem* item) 269 { 270 return RemoveItem(IndexOf(item)); 271 } 272 273 // IndexOf 274 int32 275 ResourceFile::IndexOf(ResourceItem* item) const 276 { 277 return fItems.IndexOf(item); 278 } 279 280 // ItemAt 281 ResourceItem* 282 ResourceFile::ItemAt(int32 index) const 283 { 284 return (ResourceItem*)fItems.ItemAt(index); 285 } 286 287 // CountItems 288 int32 289 ResourceFile::CountItems() const 290 { 291 return fItems.CountItems(); 292 } 293 294 // GetResourcesSize 295 uint32 296 ResourceFile::GetResourcesSize() const 297 { 298 if (!fInfoTableItem || fFile.InitCheck()) 299 throw Exception("Resource file not initialized."); 300 // header 301 uint32 size = kResourcesHeaderSize; 302 // index section 303 uint32 indexSectionSize = kResourceIndexSectionHeaderSize 304 + fResourceCount * kResourceIndexEntrySize; 305 indexSectionSize = align_value(indexSectionSize, 306 kResourceIndexSectionAlignment); 307 size += indexSectionSize; 308 // unknown section 309 size += kUnknownResourceSectionSize; 310 // data 311 uint32 dataSize = 0; 312 for (int32 i = 0; i < fResourceCount; i++) { 313 ResourceItem* item = ItemAt(i); 314 dataSize += item->GetSize(); 315 } 316 size += dataSize; 317 // info table 318 uint32 infoTableSize = 0; 319 type_code type = 0; 320 for (int32 i = 0; i < fResourceCount; i++) { 321 ResourceItem* item = ItemAt(i); 322 if (i == 0 || type != item->GetType()) { 323 if (i != 0) 324 infoTableSize += kResourceInfoSeparatorSize; 325 type = item->GetType(); 326 infoTableSize += kMinResourceInfoBlockSize; 327 } else 328 infoTableSize += kMinResourceInfoSize; 329 uint32 nameLen = strlen(item->GetName()); 330 if (nameLen != 0) 331 infoTableSize += nameLen + 1; 332 } 333 infoTableSize += kResourceInfoSeparatorSize + kResourceInfoTableEndSize; 334 size += infoTableSize; 335 return size; 336 } 337 338 // WriteResources 339 uint32 340 ResourceFile::WriteResources(void* buffer, uint32 bufferSize) 341 { 342 // calculate sizes and offsets 343 // header 344 uint32 size = kResourcesHeaderSize; 345 // index section 346 uint32 indexSectionOffset = size; 347 uint32 indexSectionSize = kResourceIndexSectionHeaderSize 348 + fResourceCount * kResourceIndexEntrySize; 349 indexSectionSize = align_value(indexSectionSize, 350 kResourceIndexSectionAlignment); 351 size += indexSectionSize; 352 // unknown section 353 uint32 unknownSectionOffset = size; 354 uint32 unknownSectionSize = kUnknownResourceSectionSize; 355 size += unknownSectionSize; 356 // data 357 uint32 dataOffset = size; 358 uint32 dataSize = 0; 359 for (int32 i = 0; i < fResourceCount; i++) { 360 ResourceItem* item = ItemAt(i); 361 dataSize += item->GetSize(); 362 } 363 size += dataSize; 364 // info table 365 uint32 infoTableOffset = size; 366 uint32 infoTableSize = 0; 367 type_code type = 0; 368 for (int32 i = 0; i < fResourceCount; i++) { 369 ResourceItem* item = ItemAt(i); 370 if (i == 0 || type != item->GetType()) { 371 if (i != 0) 372 infoTableSize += kResourceInfoSeparatorSize; 373 type = item->GetType(); 374 infoTableSize += kMinResourceInfoBlockSize; 375 } else 376 infoTableSize += kMinResourceInfoSize; 377 uint32 nameLen = strlen(item->GetName()); 378 if (nameLen != 0) 379 infoTableSize += nameLen + 1; 380 } 381 infoTableSize += kResourceInfoSeparatorSize + kResourceInfoTableEndSize; 382 size += infoTableSize; 383 // check whether the buffer is large enough 384 if (!buffer) 385 throw Exception("Supplied buffer is NULL."); 386 if (bufferSize < size) 387 throw Exception("Supplied buffer is too small."); 388 // write... 389 void* data = buffer; 390 // header 391 resources_header* resourcesHeader = (resources_header*)data; 392 resourcesHeader->rh_resources_magic = kResourcesHeaderMagic; 393 resourcesHeader->rh_resource_count = fResourceCount; 394 resourcesHeader->rh_index_section_offset = indexSectionOffset; 395 resourcesHeader->rh_admin_section_size = indexSectionOffset 396 + indexSectionSize; 397 for (int32 i = 0; i < 13; i++) 398 resourcesHeader->rh_pad[i] = 0; 399 // index section 400 // header 401 data = skip_bytes(buffer, indexSectionOffset); 402 resource_index_section_header* indexHeader 403 = (resource_index_section_header*)data; 404 indexHeader->rish_index_section_offset = indexSectionOffset; 405 indexHeader->rish_index_section_size = indexSectionSize; 406 indexHeader->rish_unknown_section_offset = unknownSectionOffset; 407 indexHeader->rish_unknown_section_size = unknownSectionSize; 408 indexHeader->rish_info_table_offset = infoTableOffset; 409 indexHeader->rish_info_table_size = infoTableSize; 410 fill_pattern(buffer, &indexHeader->rish_unused_data1, 1); 411 fill_pattern(buffer, indexHeader->rish_unused_data2, 25); 412 fill_pattern(buffer, &indexHeader->rish_unused_data3, 1); 413 // index table 414 data = skip_bytes(data, kResourceIndexSectionHeaderSize); 415 resource_index_entry* entry = (resource_index_entry*)data; 416 uint32 entryOffset = dataOffset; 417 for (int32 i = 0; i < fResourceCount; i++, entry++) { 418 ResourceItem* item = ItemAt(i); 419 uint32 entrySize = item->GetSize(); 420 entry->rie_offset = entryOffset; 421 entry->rie_size = entrySize; 422 entry->rie_pad = 0; 423 entryOffset += entrySize; 424 } 425 // padding + unknown section 426 data = skip_bytes(buffer, dataOffset); 427 fill_pattern(buffer, entry, data); 428 // data 429 for (int32 i = 0; i < fResourceCount; i++) { 430 ResourceItem* item = ItemAt(i); 431 status_t error = item->LoadData(fFile); 432 if (error != B_OK) 433 throw Exception(error, "Error loading resource data."); 434 uint32 entrySize = item->GetSize(); 435 memcpy(data, item->GetData(), entrySize); 436 data = skip_bytes(data, entrySize); 437 } 438 // info table 439 data = skip_bytes(buffer, infoTableOffset); 440 type = 0; 441 for (int32 i = 0; i < fResourceCount; i++) { 442 ResourceItem* item = ItemAt(i); 443 resource_info* info = NULL; 444 if (i == 0 || type != item->GetType()) { 445 if (i != 0) { 446 resource_info_separator* separator 447 = (resource_info_separator*)data; 448 separator->ris_value1 = 0xffffffff; 449 separator->ris_value2 = 0xffffffff; 450 data = skip_bytes(data, kResourceInfoSeparatorSize); 451 } 452 type = item->GetType(); 453 resource_info_block* infoBlock = (resource_info_block*)data; 454 infoBlock->rib_type = type; 455 info = infoBlock->rib_info; 456 } else 457 info = (resource_info*)data; 458 // info 459 info->ri_id = item->GetID(); 460 info->ri_index = i + 1; 461 info->ri_name_size = 0; 462 data = info->ri_name; 463 uint32 nameLen = strlen(item->GetName()); 464 if (nameLen != 0) { 465 memcpy(info->ri_name, item->GetName(), nameLen + 1); 466 data = skip_bytes(data, nameLen + 1); 467 info->ri_name_size = nameLen + 1; 468 } 469 } 470 // separator 471 resource_info_separator* separator = (resource_info_separator*)data; 472 separator->ris_value1 = 0xffffffff; 473 separator->ris_value2 = 0xffffffff; 474 // table end 475 data = skip_bytes(data, kResourceInfoSeparatorSize); 476 resource_info_table_end* tableEnd = (resource_info_table_end*)data; 477 void* infoTable = skip_bytes(buffer, infoTableOffset); 478 tableEnd->rite_check_sum = calculate_checksum(infoTable, 479 infoTableSize - kResourceInfoTableEndSize); 480 tableEnd->rite_terminator = 0; 481 // final check 482 data = skip_bytes(data, kResourceInfoTableEndSize); 483 uint32 bytesWritten = (char*)data - (char*)buffer; 484 if (bytesWritten != size) { 485 throw Exception("Bad boy error: Wrote %lu bytes, though supposed to " 486 "write %lu bytes.", bytesWritten, size); 487 } 488 return size; 489 } 490 491 // WriteTest 492 void 493 ResourceFile::WriteTest() 494 { 495 uint32 size = GetResourcesSize(); 496 if (size != fFileSize) { 497 throw Exception("Calculated resources size differs from actual size " 498 "in file: %lu vs %Ld.", size, fFileSize); 499 } 500 char* buffer1 = new char[size]; 501 char* buffer2 = new char[size]; 502 try { 503 WriteResources(buffer1, size); 504 read_exactly(fFile, 0, buffer2, size, 505 "Write test: Error reading resources."); 506 for (uint32 i = 0; i < size; i++) { 507 if (buffer1[i] != buffer2[i]) { 508 off_t filePosition = fFile.GetOffset() + i; 509 throw Exception("Written resources differ from those in file. " 510 "First difference at byte %lu (file position " 511 "%Ld): %x vs %x.", i, filePosition, 512 (int)buffer1[i] & 0xff, 513 (int)buffer2[i] & 0xff); 514 } 515 } 516 } catch (Exception exception) { 517 delete[] buffer1; 518 delete[] buffer2; 519 throw exception; 520 } 521 delete[] buffer1; 522 delete[] buffer2; 523 } 524 525 526 527 // PrintToStream 528 void 529 ResourceFile::PrintToStream(bool longInfo) 530 { 531 if (longInfo) { 532 off_t resourcesOffset = fFile.GetOffset(); 533 printf("ResourceFile:\n"); 534 printf("file type : %s\n", kFileTypeNames[fFileType]); 535 printf("endianess : %s\n", 536 (fHostEndianess == (bool)B_HOST_IS_LENDIAN) ? "little" : "big"); 537 printf("resource section offset: 0x%08Lx (%Ld)\n", resourcesOffset, 538 resourcesOffset); 539 if (fInfoTableItem) { 540 int32 offset = fInfoTableItem->GetOffset(); 541 int32 size = fInfoTableItem->GetSize(); 542 printf("resource info table : offset: 0x%08lx (%ld), " 543 "size: 0x%08lx (%ld)\n", offset, offset, size, size); 544 } 545 printf("number of resources : %ld\n", fResourceCount); 546 for (int32 i = 0; i < fResourceCount; i++) { 547 ResourceItem* item = ItemAt(i); 548 item->PrintToStream(); 549 } 550 } else { 551 printf(" Type ID Size Name\n"); 552 printf(" ------ ----- -------- --------------------\n"); 553 for (int32 i = 0; i < fResourceCount; i++) { 554 ResourceItem* item = ItemAt(i); 555 type_code type = item->GetType(); 556 char typeName[4] = { type >> 24, (type >> 16) & 0xff, 557 (type >> 8) & 0xff, type & 0xff }; 558 printf(" '%.4s' %5ld %8lu %s\n", typeName, item->GetID(), 559 item->GetSize(), item->GetName()); 560 } 561 } 562 } 563 564 565 // _InitFile 566 void 567 ResourceFile::_InitFile(BFile& file) 568 { 569 status_t error = B_OK; 570 fFile.Unset(); 571 // read the first four bytes, and check, if they identify a resource file 572 char magic[4]; 573 read_exactly(file, 0, magic, 4, "Failed to read magic number."); 574 if (!memcmp(magic, kX86ResourceFileMagic, 4)) { 575 // x86 resource file 576 fHostEndianess = B_HOST_IS_LENDIAN; 577 fFileType = FILE_TYPE_X86_RESOURCE; 578 fFile.SetTo(file, kX86ResourcesOffset); 579 } else if (!memcmp(magic, kPEFFileMagic1, 4)) { 580 PEFContainerHeader pefHeader; 581 read_exactly(file, 0, &pefHeader, kPEFContainerHeaderSize, 582 "Failed to read PEF container header."); 583 if (!memcmp(pefHeader.tag2, kPPCResourceFileMagic, 4)) { 584 // PPC resource file 585 fHostEndianess = B_HOST_IS_BENDIAN; 586 fFileType = FILE_TYPE_PPC_RESOURCE; 587 fFile.SetTo(file, kPPCResourcesOffset); 588 } else if (!memcmp(pefHeader.tag2, kPEFFileMagic2, 4)) { 589 // PEF file 590 fFileType = FILE_TYPE_PEF; 591 _InitPEFFile(file, pefHeader); 592 } else 593 throw Exception("File is not a resource file."); 594 } else if (!memcmp(magic, kELFFileMagic, 4)) { 595 // ELF file 596 fFileType = FILE_TYPE_ELF; 597 _InitELFFile(file); 598 } else if (!memcmp(magic, kX86ResourceFileMagic, 2)) { 599 // x86 resource file with screwed magic? 600 Warnings::AddCurrentWarning("File magic is 0x%08lx. Should be 0x%08lx " 601 "for x86 resource file. Try anyway.", 602 ntohl(*(uint32*)magic), 603 ntohl(*(uint32*)kX86ResourceFileMagic)); 604 fHostEndianess = B_HOST_IS_LENDIAN; 605 fFileType = FILE_TYPE_X86_RESOURCE; 606 fFile.SetTo(file, kX86ResourcesOffset); 607 } else 608 throw Exception("File is not a resource file."); 609 error = fFile.InitCheck(); 610 if (error != B_OK) 611 throw Exception(error, "Failed to initialize resource file."); 612 // get the file size 613 fFileSize = 0; 614 error = fFile.GetSize(&fFileSize); 615 if (error != B_OK) 616 throw Exception(error, "Failed to get the file size."); 617 } 618 619 // _InitELFFile 620 void 621 ResourceFile::_InitELFFile(BFile& file) 622 { 623 status_t error = B_OK; 624 // get the file size 625 off_t fileSize = 0; 626 error = file.GetSize(&fileSize); 627 if (error != B_OK) 628 throw Exception(error, "Failed to get the file size."); 629 // read ELF header 630 Elf32_Ehdr fileHeader; 631 read_exactly(file, 0, &fileHeader, sizeof(Elf32_Ehdr), 632 "Failed to read ELF header."); 633 // check data encoding (endianess) 634 switch (fileHeader.e_ident[EI_DATA]) { 635 case ELFDATA2LSB: 636 fHostEndianess = B_HOST_IS_LENDIAN; 637 break; 638 case ELFDATA2MSB: 639 fHostEndianess = B_HOST_IS_BENDIAN; 640 break; 641 default: 642 case ELFDATANONE: 643 throw Exception("Unsupported ELF data encoding."); 644 break; 645 } 646 // get the header values 647 uint32 headerSize = _GetUInt16(fileHeader.e_ehsize); 648 uint32 programHeaderTableOffset = _GetUInt32(fileHeader.e_phoff); 649 uint32 programHeaderSize = _GetUInt16(fileHeader.e_phentsize); 650 uint32 programHeaderCount = _GetUInt16(fileHeader.e_phnum); 651 uint32 sectionHeaderTableOffset = _GetUInt32(fileHeader.e_shoff); 652 uint32 sectionHeaderSize = _GetUInt16(fileHeader.e_shentsize); 653 uint32 sectionHeaderCount = _GetUInt16(fileHeader.e_shnum); 654 bool hasProgramHeaderTable = (programHeaderTableOffset != 0); 655 bool hasSectionHeaderTable = (sectionHeaderTableOffset != 0); 656 //printf("headerSize : %lu\n", headerSize); 657 //printf("programHeaderTableOffset: %lu\n", programHeaderTableOffset); 658 //printf("programHeaderSize : %lu\n", programHeaderSize); 659 //printf("programHeaderCount : %lu\n", programHeaderCount); 660 //printf("sectionHeaderTableOffset: %lu\n", sectionHeaderTableOffset); 661 //printf("sectionHeaderSize : %lu\n", sectionHeaderSize); 662 //printf("sectionHeaderCount : %lu\n", sectionHeaderCount); 663 // check the sanity of the header values 664 // ELF header size 665 if (headerSize < sizeof(Elf32_Ehdr) || headerSize > kMaxELFHeaderSize) { 666 throw Exception("Invalid ELF header: invalid ELF header size: %lu.", 667 headerSize); 668 } 669 uint32 resourceOffset = headerSize; 670 uint32 resourceAlignment = 0; 671 // program header table offset and entry count/size 672 uint32 programHeaderTableSize = 0; 673 if (hasProgramHeaderTable) { 674 if (programHeaderTableOffset < headerSize 675 || programHeaderTableOffset > fileSize) { 676 throw Exception("Invalid ELF header: invalid program header table " 677 "offset: %lu.", programHeaderTableOffset); 678 } 679 programHeaderTableSize = programHeaderSize * programHeaderCount; 680 if (programHeaderSize < sizeof(Elf32_Phdr) 681 || programHeaderTableOffset + programHeaderTableSize > fileSize) { 682 throw Exception("Invalid ELF header: program header table exceeds " 683 "file: %lu.", 684 programHeaderTableOffset + programHeaderTableSize); 685 } 686 resourceOffset = max(resourceOffset, programHeaderTableOffset 687 + programHeaderTableSize); 688 // iterate through the program headers 689 for (int32 i = 0; i < (int32)programHeaderCount; i++) { 690 uint32 shOffset = programHeaderTableOffset + i * programHeaderSize; 691 Elf32_Phdr programHeader; 692 read_exactly(file, shOffset, &programHeader, sizeof(Elf32_Shdr), 693 "Failed to read ELF program header."); 694 // get the header values 695 uint32 type = _GetUInt32(programHeader.p_type); 696 uint32 offset = _GetUInt32(programHeader.p_offset); 697 uint32 size = _GetUInt32(programHeader.p_filesz); 698 uint32 alignment = _GetUInt32(programHeader.p_align); 699 //printf("segment: type: %ld, offset: %lu, size: %lu, alignment: %lu\n", 700 //type, offset, size, alignment); 701 // check the values 702 // PT_NULL marks the header unused, 703 if (type != PT_NULL) { 704 if (/*offset < headerSize ||*/ offset > fileSize) { 705 throw Exception("Invalid ELF program header: invalid " 706 "program offset: %lu.", offset); 707 } 708 uint32 segmentEnd = offset + size; 709 if (segmentEnd > fileSize) { 710 throw Exception("Invalid ELF section header: segment " 711 "exceeds file: %lu.", segmentEnd); 712 } 713 resourceOffset = max(resourceOffset, segmentEnd); 714 resourceAlignment = max(resourceAlignment, alignment); 715 } 716 } 717 } 718 // section header table offset and entry count/size 719 uint32 sectionHeaderTableSize = 0; 720 if (hasSectionHeaderTable) { 721 if (sectionHeaderTableOffset < headerSize 722 || sectionHeaderTableOffset > fileSize) { 723 throw Exception("Invalid ELF header: invalid section header table " 724 "offset: %lu.", sectionHeaderTableOffset); 725 } 726 sectionHeaderTableSize = sectionHeaderSize * sectionHeaderCount; 727 if (sectionHeaderSize < sizeof(Elf32_Shdr) 728 || sectionHeaderTableOffset + sectionHeaderTableSize > fileSize) { 729 throw Exception("Invalid ELF header: section header table exceeds " 730 "file: %lu.", 731 sectionHeaderTableOffset + sectionHeaderTableSize); 732 } 733 resourceOffset = max(resourceOffset, sectionHeaderTableOffset 734 + sectionHeaderTableSize); 735 // iterate through the section headers 736 for (int32 i = 0; i < (int32)sectionHeaderCount; i++) { 737 uint32 shOffset = sectionHeaderTableOffset + i * sectionHeaderSize; 738 Elf32_Shdr sectionHeader; 739 read_exactly(file, shOffset, §ionHeader, sizeof(Elf32_Shdr), 740 "Failed to read ELF section header."); 741 // get the header values 742 uint32 type = _GetUInt32(sectionHeader.sh_type); 743 uint32 offset = _GetUInt32(sectionHeader.sh_offset); 744 uint32 size = _GetUInt32(sectionHeader.sh_size); 745 //printf("section: type: %ld, offset: %lu, size: %lu\n", type, offset, size); 746 // check the values 747 // SHT_NULL marks the header unused, 748 // SHT_NOBITS sections take no space in the file 749 if (type != SHT_NULL && type != SHT_NOBITS) { 750 if (offset < headerSize || offset > fileSize) { 751 throw Exception("Invalid ELF section header: invalid " 752 "section offset: %lu.", offset); 753 } 754 uint32 sectionEnd = offset + size; 755 if (sectionEnd > fileSize) { 756 throw Exception("Invalid ELF section header: section " 757 "exceeds file: %lu.", sectionEnd); 758 } 759 resourceOffset = max(resourceOffset, sectionEnd); 760 } 761 } 762 } 763 //printf("resourceOffset: %lu\n", resourceOffset); 764 // align the offset 765 if (resourceAlignment < kELFMinResourceAlignment) 766 resourceAlignment = kELFMinResourceAlignment; 767 if (resourceAlignment > kELFMaxResourceAlignment) { 768 throw Exception("The ELF object file requires an invalid alignment: " 769 "%lu.", resourceAlignment); 770 } 771 resourceOffset = align_value(resourceOffset, resourceAlignment); 772 //printf("resourceOffset: %lu\n", resourceOffset); 773 if (resourceOffset >= fileSize) 774 throw Exception("The ELF object file does not contain resources."); 775 // fine, init the offset file 776 fFile.SetTo(file, resourceOffset); 777 } 778 779 // _InitPEFFile 780 void 781 ResourceFile::_InitPEFFile(BFile& file, const PEFContainerHeader& pefHeader) 782 { 783 status_t error = B_OK; 784 // get the file size 785 off_t fileSize = 0; 786 error = file.GetSize(&fileSize); 787 if (error != B_OK) 788 throw Exception(error, "Failed to get the file size."); 789 // check architecture -- we support PPC only 790 if (memcmp(pefHeader.architecture, kPEFArchitecturePPC, 4)) 791 throw Exception("PEF file architecture is not PPC."); 792 fHostEndianess = B_HOST_IS_BENDIAN; 793 // get the section count 794 uint16 sectionCount = _GetUInt16(pefHeader.sectionCount); 795 // iterate through the PEF sections headers 796 uint32 sectionHeaderTableOffset = kPEFContainerHeaderSize; 797 uint32 sectionHeaderTableEnd 798 = sectionHeaderTableOffset + sectionCount * kPEFSectionHeaderSize; 799 uint32 resourceOffset = sectionHeaderTableEnd; 800 for (int32 i = 0; i < (int32)sectionCount; i++) { 801 uint32 shOffset = sectionHeaderTableOffset + i * kPEFSectionHeaderSize; 802 PEFSectionHeader sectionHeader; 803 read_exactly(file, shOffset, §ionHeader, kPEFSectionHeaderSize, 804 "Failed to read PEF section header."); 805 // get the header values 806 uint32 offset = _GetUInt32(sectionHeader.containerOffset); 807 uint32 size = _GetUInt32(sectionHeader.packedSize); 808 // check the values 809 if (offset < sectionHeaderTableEnd || offset > fileSize) { 810 throw Exception("Invalid PEF section header: invalid " 811 "section offset: %lu.", offset); 812 } 813 uint32 sectionEnd = offset + size; 814 if (sectionEnd > fileSize) { 815 throw Exception("Invalid PEF section header: section " 816 "exceeds file: %lu.", sectionEnd); 817 } 818 resourceOffset = max(resourceOffset, sectionEnd); 819 } 820 // init the offset file 821 fFile.SetTo(file, resourceOffset); 822 } 823 824 // _ReadHeader 825 void 826 ResourceFile::_ReadHeader() 827 { 828 // read the header 829 resources_header header; 830 read_exactly(fFile, 0, &header, kResourcesHeaderSize, 831 "Failed to read the header."); 832 // check the header 833 // magic 834 uint32 magic = _GetUInt32(header.rh_resources_magic); 835 if (magic == kResourcesHeaderMagic) { 836 // everything is fine 837 } else if (B_SWAP_INT32(magic) == kResourcesHeaderMagic) { 838 const char* endianessStr[2] = { "little", "big" }; 839 int32 endianess 840 = (fHostEndianess == ((bool)B_HOST_IS_LENDIAN ? 0 : 1)); 841 Warnings::AddCurrentWarning("Endianess seems to be %s, although %s " 842 "was expected.", 843 endianessStr[1 - endianess], 844 endianessStr[endianess]); 845 fHostEndianess = !fHostEndianess; 846 } else 847 throw Exception("Invalid resources header magic."); 848 // resource count 849 uint32 resourceCount = _GetUInt32(header.rh_resource_count); 850 if (resourceCount > kMaxResourceCount) 851 throw Exception("Bad number of resources."); 852 // index section offset 853 uint32 indexSectionOffset = _GetUInt32(header.rh_index_section_offset); 854 if (indexSectionOffset != kResourceIndexSectionOffset) { 855 throw Exception("Unexpected resource index section offset. Is: %lu, " 856 "should be: %lu.", indexSectionOffset, 857 kResourceIndexSectionOffset); 858 } 859 // admin section size 860 uint32 indexSectionSize = kResourceIndexSectionHeaderSize 861 + kResourceIndexEntrySize * resourceCount; 862 indexSectionSize = align_value(indexSectionSize, 863 kResourceIndexSectionAlignment); 864 uint32 adminSectionSize = _GetUInt32(header.rh_admin_section_size); 865 if (adminSectionSize != indexSectionOffset + indexSectionSize) { 866 throw Exception("Unexpected resource admin section size. Is: %lu, " 867 "should be: %lu.", adminSectionSize, 868 indexSectionOffset + indexSectionSize); 869 } 870 // set the resource count 871 fResourceCount = resourceCount; 872 } 873 874 // _ReadIndex 875 void 876 ResourceFile::_ReadIndex() 877 { 878 // read the header 879 resource_index_section_header header; 880 read_exactly(fFile, kResourceIndexSectionOffset, &header, 881 kResourceIndexSectionHeaderSize, 882 "Failed to read the resource index section header."); 883 // check the header 884 // index section offset 885 uint32 indexSectionOffset = _GetUInt32(header.rish_index_section_offset); 886 if (indexSectionOffset != kResourceIndexSectionOffset) { 887 throw Exception("Unexpected resource index section offset. Is: %lu, " 888 "should be: %lu.", indexSectionOffset, 889 kResourceIndexSectionOffset); 890 } 891 // index section size 892 uint32 expectedIndexSectionSize = kResourceIndexSectionHeaderSize 893 + kResourceIndexEntrySize * fResourceCount; 894 expectedIndexSectionSize = align_value(expectedIndexSectionSize, 895 kResourceIndexSectionAlignment); 896 uint32 indexSectionSize = _GetUInt32(header.rish_index_section_size); 897 if (indexSectionSize != expectedIndexSectionSize) { 898 throw Exception("Unexpected resource index section size. Is: %lu, " 899 "should be: %lu.", indexSectionSize, 900 expectedIndexSectionSize); 901 } 902 // unknown section offset 903 uint32 unknownSectionOffset 904 = _GetUInt32(header.rish_unknown_section_offset); 905 if (unknownSectionOffset != indexSectionOffset + indexSectionSize) { 906 throw Exception("Unexpected resource index section size. Is: %lu, " 907 "should be: %lu.", unknownSectionOffset, 908 indexSectionOffset + indexSectionSize); 909 } 910 // unknown section size 911 uint32 unknownSectionSize = _GetUInt32(header.rish_unknown_section_size); 912 if (unknownSectionSize != kUnknownResourceSectionSize) { 913 throw Exception("Unexpected resource index section offset. Is: %lu, " 914 "should be: %lu.", unknownSectionOffset, 915 kUnknownResourceSectionSize); 916 } 917 // info table offset and size 918 uint32 infoTableOffset = _GetUInt32(header.rish_info_table_offset); 919 uint32 infoTableSize = _GetUInt32(header.rish_info_table_size); 920 if (infoTableOffset + infoTableSize > fFileSize) 921 throw Exception("Invalid info table location."); 922 fInfoTableItem = new ResourceItem; 923 fInfoTableItem->SetLocation(infoTableOffset, infoTableSize); 924 // read the index entries 925 uint32 indexTableOffset = indexSectionOffset 926 + kResourceIndexSectionHeaderSize; 927 int32 maxResourceCount = (unknownSectionOffset - indexTableOffset) 928 / kResourceIndexEntrySize; 929 int32 actualResourceCount = 0; 930 bool tableEndReached = false; 931 for (int32 i = 0; !tableEndReached && i < maxResourceCount; i++) { 932 // read one entry 933 tableEndReached = !_ReadIndexEntry(i, indexTableOffset, 934 (i >= fResourceCount)); 935 if (!tableEndReached) 936 actualResourceCount++; 937 } 938 // check resource count 939 if (actualResourceCount != fResourceCount) { 940 if (actualResourceCount > fResourceCount) { 941 Warnings::AddCurrentWarning("Resource index table contains " 942 "%ld entries, although it should be " 943 "%ld only.", actualResourceCount, 944 fResourceCount); 945 } 946 fResourceCount = actualResourceCount; 947 } 948 } 949 950 // _ReadIndexEntry 951 bool 952 ResourceFile::_ReadIndexEntry(int32 index, uint32 tableOffset, bool peekAhead) 953 { 954 bool result = true; 955 resource_index_entry entry; 956 // read one entry 957 off_t entryOffset = tableOffset + index * kResourceIndexEntrySize; 958 read_exactly(fFile, entryOffset, &entry, kResourceIndexEntrySize, 959 "Failed to read a resource index entry."); 960 // check, if the end is reached early 961 if (result && check_pattern(entryOffset, &entry, 962 kResourceIndexEntrySize / 4, fHostEndianess)) { 963 if (!peekAhead) { 964 Warnings::AddCurrentWarning("Unexpected end of resource index " 965 "table at index: %ld (/%ld).", 966 index + 1, fResourceCount); 967 } 968 result = false; 969 } 970 uint32 offset = _GetUInt32(entry.rie_offset); 971 uint32 size = _GetUInt32(entry.rie_size); 972 // check the location 973 if (result && offset + size > fFileSize) { 974 if (peekAhead) { 975 Warnings::AddCurrentWarning("Invalid data after resource index " 976 "table."); 977 } else { 978 throw Exception("Invalid resource index entry: index: %ld, " 979 "offset: %lu (%lx), size: %lu (%lx).", index + 1, 980 offset, offset, size, size); 981 } 982 result = false; 983 } 984 // add the entry 985 if (result) { 986 ResourceItem* item = new ResourceItem; 987 item->SetLocation(offset, size); 988 AddItem(item, index); 989 } 990 return result; 991 } 992 993 // _ReadInfoTable 994 void 995 ResourceFile::_ReadInfoTable() 996 { 997 status_t error = B_OK; 998 error = fInfoTableItem->LoadData(fFile); 999 if (error != B_OK) 1000 throw Exception(error, "Failed to read resource info table."); 1001 const void* tableData = fInfoTableItem->GetData(); 1002 int32 dataSize = fInfoTableItem->GetSize(); 1003 // 1004 bool* readIndices = new bool[fResourceCount + 1]; // + 1 => always > 0 1005 for (int32 i = 0; i < fResourceCount; i++) 1006 readIndices[i] = false; 1007 AutoDeleter<bool> deleter(readIndices, true); 1008 MemArea area(tableData, dataSize); 1009 const void* data = tableData; 1010 // check the table end/check sum 1011 if (_ReadInfoTableEnd(data, dataSize)) 1012 dataSize -= kResourceInfoTableEndSize; 1013 // read the infos 1014 int32 resourceIndex = 1; 1015 uint32 minRemainderSize 1016 = kMinResourceInfoBlockSize + kResourceInfoSeparatorSize; 1017 while (area.check(data, minRemainderSize)) { 1018 // read a resource block 1019 if (!area.check(data, kMinResourceInfoBlockSize)) { 1020 throw Exception("Unexpected end of resource info table at index " 1021 "%ld.", resourceIndex); 1022 } 1023 const resource_info_block* infoBlock 1024 = (const resource_info_block*)data; 1025 type_code type = _GetUInt32(infoBlock->rib_type); 1026 // read the infos of this block 1027 const resource_info* info = infoBlock->rib_info; 1028 while (info) { 1029 data = _ReadResourceInfo(area, info, type, readIndices); 1030 // prepare for next iteration, if there is another info 1031 if (!area.check(data, kResourceInfoSeparatorSize)) { 1032 throw Exception("Unexpected end of resource info table after " 1033 "index %ld.", resourceIndex); 1034 } 1035 const resource_info_separator* separator 1036 = (const resource_info_separator*)data; 1037 if (_GetUInt32(separator->ris_value1) == 0xffffffff 1038 && _GetUInt32(separator->ris_value2) == 0xffffffff) { 1039 // info block ends 1040 info = NULL; 1041 data = skip_bytes(data, kResourceInfoSeparatorSize); 1042 } else { 1043 // another info follows 1044 info = (const resource_info*)data; 1045 } 1046 resourceIndex++; 1047 } 1048 // end of the info block 1049 } 1050 // handle special case: empty resource info table 1051 if (resourceIndex == 1) { 1052 if (!area.check(data, kResourceInfoSeparatorSize)) { 1053 throw Exception("Unexpected end of resource info table."); 1054 } 1055 const resource_info_separator* tableTerminator 1056 = (const resource_info_separator*)data; 1057 if (_GetUInt32(tableTerminator->ris_value1) != 0xffffffff 1058 || _GetUInt32(tableTerminator->ris_value2) != 0xffffffff) { 1059 throw Exception("The resource info table ought to be empty, but " 1060 "is not properly terminated."); 1061 } 1062 data = skip_bytes(data, kResourceInfoSeparatorSize); 1063 } 1064 // Check, if the correct number of bytes are remaining. 1065 uint32 bytesLeft = (const char*)tableData + dataSize - (const char*)data; 1066 if (bytesLeft != 0) { 1067 throw Exception("Error at the end of the resource info table: %lu " 1068 "bytes are remaining.", bytesLeft); 1069 } 1070 // check, if all items have been initialized 1071 for (int32 i = fResourceCount - 1; i >= 0; i--) { 1072 if (!readIndices[i]) { 1073 Warnings::AddCurrentWarning("Resource item at index %ld " 1074 "has no info. Item removed.", i + 1); 1075 if (ResourceItem* item = RemoveItem(i)) 1076 delete item; 1077 fResourceCount--; 1078 } 1079 } 1080 } 1081 1082 // _ReadInfoTableEnd 1083 bool 1084 ResourceFile::_ReadInfoTableEnd(const void* data, int32 dataSize) 1085 { 1086 bool hasTableEnd = true; 1087 if ((uint32)dataSize < kResourceInfoSeparatorSize) 1088 throw Exception("Info table is too short."); 1089 if ((uint32)dataSize < kResourceInfoTableEndSize) 1090 hasTableEnd = false; 1091 if (hasTableEnd) { 1092 const resource_info_table_end* tableEnd 1093 = (const resource_info_table_end*) 1094 skip_bytes(data, dataSize - kResourceInfoTableEndSize); 1095 if (_GetInt32(tableEnd->rite_terminator) != 0) 1096 hasTableEnd = false; 1097 if (hasTableEnd) { 1098 dataSize -= kResourceInfoTableEndSize; 1099 // checksum 1100 uint32 checkSum = calculate_checksum(data, dataSize); 1101 uint32 fileCheckSum = _GetUInt32(tableEnd->rite_check_sum); 1102 if (checkSum != fileCheckSum) { 1103 throw Exception("Invalid resource info table check sum: In " 1104 "file: %lx, calculated: %lx.", fileCheckSum, 1105 checkSum); 1106 } 1107 } 1108 } 1109 if (!hasTableEnd) 1110 Warnings::AddCurrentWarning("resource info table has no check sum."); 1111 return hasTableEnd; 1112 } 1113 1114 // _ReadResourceInfo 1115 const void* 1116 ResourceFile::_ReadResourceInfo(const MemArea& area, const resource_info* info, 1117 type_code type, bool* readIndices) 1118 { 1119 int32 id = _GetInt32(info->ri_id); 1120 int32 index = _GetInt32(info->ri_index); 1121 uint16 nameSize = _GetUInt16(info->ri_name_size); 1122 const char* name = info->ri_name; 1123 // check the values 1124 bool ignore = false; 1125 // index 1126 if (index < 1 || index > fResourceCount) { 1127 Warnings::AddCurrentWarning("Invalid index field in resource " 1128 "info table: %lu.", index); 1129 ignore = true; 1130 } 1131 if (!ignore) { 1132 if (readIndices[index - 1]) { 1133 throw Exception("Multiple resource infos with the same index " 1134 "field: %ld.", index); 1135 } 1136 readIndices[index - 1] = true; 1137 } 1138 // name size 1139 if (!area.check(name, nameSize)) { 1140 throw Exception("Invalid name size (%d) for index %ld in " 1141 "resource info table.", (int)nameSize, index); 1142 } 1143 // check, if name is null terminated 1144 if (name[nameSize - 1] != 0) { 1145 Warnings::AddCurrentWarning("Name for index %ld in " 1146 "resource info table is not null " 1147 "terminated.", index); 1148 } 1149 // set the values 1150 if (!ignore) { 1151 BString resourceName(name, nameSize); 1152 if (ResourceItem* item = ItemAt(index - 1)) 1153 item->SetIdentity(type, id, resourceName.String()); 1154 else { 1155 throw Exception("Unexpected error: No resource item at index " 1156 "%ld.", index); 1157 } 1158 } 1159 return skip_bytes(name, nameSize); 1160 } 1161