vfs: check userland buffers before reading them.
[haiku.git] / src / tools / restest / ResourceFile.cpp
blobffb69d94758944aa2717b445377f77ac34da994c
1 // ResourceFile.cpp
3 #include "ResourceFile.h"
5 #include <algobase.h>
6 #include <stdio.h>
8 #include "Elf.h"
9 #include "Exception.h"
10 #include "Pef.h"
11 #include "ResourceItem.h"
12 #include "ResourcesDefs.h"
13 #include "Warnings.h"
15 // ELF defs
16 static const uint32 kMaxELFHeaderSize = sizeof(Elf32_Ehdr) + 32;
17 static const char kELFFileMagic[4] = { 0x7f, 'E', 'L', 'F' };
19 // sanity bounds
20 static const uint32 kMaxResourceCount = 10000;
21 static const uint32 kELFMaxResourceAlignment = 1024 * 1024 * 10; // 10 MB
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,
32 const char* kFileTypeNames[] = {
33 "unknown",
34 "x86 resource file",
35 "PPC resource file",
36 "ELF object file",
37 "PEF object file",
41 // helper functions/classes
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)
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);
61 // align_value
62 template<typename TV, typename TA>
63 static inline
65 align_value(const TV& value, const TA& alignment)
67 return ((value + alignment - 1) / alignment) * alignment;
70 // calculate_checksum
71 static
72 uint32
73 calculate_checksum(const void* data, uint32 size)
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;
86 return checkSum;
89 // skip_bytes
90 static inline
91 const void*
92 skip_bytes(const void* buffer, int32 offset)
94 return (const char*)buffer + offset;
97 // skip_bytes
98 static inline
99 void*
100 skip_bytes(void* buffer, int32 offset)
102 return (char*)buffer + offset;
105 // fill_pattern
106 static
107 void
108 fill_pattern(uint32 byteOffset, void* _buffer, uint32 count)
110 uint32* buffer = (uint32*)_buffer;
111 for (uint32 i = 0; i < count; i++)
112 buffer[i] = kUnusedResourceDataPattern[(byteOffset / 4 + i) % 3];
115 // fill_pattern
116 static
117 void
118 fill_pattern(const void* dataBegin, void* buffer, uint32 count)
120 fill_pattern((char*)buffer - (const char*)dataBegin, buffer, count);
123 // fill_pattern
124 static
125 void
126 fill_pattern(const void* dataBegin, void* buffer, const void* bufferEnd)
128 fill_pattern(dataBegin, buffer,
129 ((const char*)bufferEnd - (char*)buffer) / 4);
132 // check_pattern
133 static
134 bool
135 check_pattern(uint32 byteOffset, void* _buffer, uint32 count,
136 bool hostEndianess)
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]);
147 return result;
150 // MemArea
151 struct MemArea {
152 MemArea(const void* data, uint32 size) : data(data), size(size) {}
154 inline bool check(const void* _current, uint32 skip = 0) const
156 const char* start = (const char*)data;
157 const char* current = (const char*)_current;
158 return (start <= current && start + size >= current + skip);
161 const void* data;
162 uint32 size;
165 // AutoDeleter
166 template<typename C>
167 struct AutoDeleter {
168 AutoDeleter(C* object, bool array = false) : object(object), array(array)
172 ~AutoDeleter()
174 if (array)
175 delete[] object;
176 else
177 delete object;
180 C* object;
181 bool array;
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)
197 // destructor
198 ResourceFile::~ResourceFile()
200 Unset();
203 // Init
204 void
205 ResourceFile::Init(BFile& file)
207 Unset();
208 try {
209 _InitFile(file);
210 _ReadHeader();
211 _ReadIndex();
212 _ReadInfoTable();
213 } catch (Exception exception) {
214 Unset();
215 throw exception;
219 // Unset
220 void
221 ResourceFile::Unset()
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;
239 // InitCheck
240 status_t
241 ResourceFile::InitCheck() const
243 return fFile.InitCheck();
246 // AddItem
247 bool
248 ResourceFile::AddItem(ResourceItem* item, int32 index)
250 bool result = false;
251 if (item) {
252 if (index < 0 || index > CountItems())
253 index = CountItems();
254 result = fItems.AddItem(item);
256 return result;
259 // RemoveItem
260 ResourceItem*
261 ResourceFile::RemoveItem(int32 index)
263 return (ResourceItem*)fItems.RemoveItem(index);
266 // RemoveItem
267 bool
268 ResourceFile::RemoveItem(ResourceItem* item)
270 return RemoveItem(IndexOf(item));
273 // IndexOf
274 int32
275 ResourceFile::IndexOf(ResourceItem* item) const
277 return fItems.IndexOf(item);
280 // ItemAt
281 ResourceItem*
282 ResourceFile::ItemAt(int32 index) const
284 return (ResourceItem*)fItems.ItemAt(index);
287 // CountItems
288 int32
289 ResourceFile::CountItems() const
291 return fItems.CountItems();
294 // GetResourcesSize
295 uint32
296 ResourceFile::GetResourcesSize() const
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();
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;
333 infoTableSize += kResourceInfoSeparatorSize + kResourceInfoTableEndSize;
334 size += infoTableSize;
335 return size;
338 // WriteResources
339 uint32
340 ResourceFile::WriteResources(void* buffer, uint32 bufferSize)
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();
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;
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;
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);
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);
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;
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);
488 return size;
491 // WriteTest
492 void
493 ResourceFile::WriteTest()
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);
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);
516 } catch (Exception exception) {
517 delete[] buffer1;
518 delete[] buffer2;
519 throw exception;
521 delete[] buffer1;
522 delete[] buffer2;
527 // PrintToStream
528 void
529 ResourceFile::PrintToStream(bool longInfo)
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);
545 printf("number of resources : %ld\n", fResourceCount);
546 for (int32 i = 0; i < fResourceCount; i++) {
547 ResourceItem* item = ItemAt(i);
548 item->PrintToStream();
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());
565 // _InitFile
566 void
567 ResourceFile::_InitFile(BFile& file)
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.");
619 // _InitELFFile
620 void
621 ResourceFile::_InitELFFile(BFile& file)
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;
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);
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);
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);
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);
708 uint32 segmentEnd = offset + size;
709 if (segmentEnd > fileSize) {
710 throw Exception("Invalid ELF section header: segment "
711 "exceeds file: %lu.", segmentEnd);
713 resourceOffset = max(resourceOffset, segmentEnd);
714 resourceAlignment = max(resourceAlignment, alignment);
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);
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);
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, &sectionHeader, 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);
754 uint32 sectionEnd = offset + size;
755 if (sectionEnd > fileSize) {
756 throw Exception("Invalid ELF section header: section "
757 "exceeds file: %lu.", sectionEnd);
759 resourceOffset = max(resourceOffset, sectionEnd);
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);
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);
779 // _InitPEFFile
780 void
781 ResourceFile::_InitPEFFile(BFile& file, const PEFContainerHeader& pefHeader)
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, &sectionHeader, 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);
813 uint32 sectionEnd = offset + size;
814 if (sectionEnd > fileSize) {
815 throw Exception("Invalid PEF section header: section "
816 "exceeds file: %lu.", sectionEnd);
818 resourceOffset = max(resourceOffset, sectionEnd);
820 // init the offset file
821 fFile.SetTo(file, resourceOffset);
824 // _ReadHeader
825 void
826 ResourceFile::_ReadHeader()
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);
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);
870 // set the resource count
871 fResourceCount = resourceCount;
874 // _ReadIndex
875 void
876 ResourceFile::_ReadIndex()
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);
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);
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);
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);
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++;
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);
946 fResourceCount = actualResourceCount;
950 // _ReadIndexEntry
951 bool
952 ResourceFile::_ReadIndexEntry(int32 index, uint32 tableOffset, bool peekAhead)
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);
968 result = false;
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);
982 result = false;
984 // add the entry
985 if (result) {
986 ResourceItem* item = new ResourceItem;
987 item->SetLocation(offset, size);
988 AddItem(item, index);
990 return result;
993 // _ReadInfoTable
994 void
995 ResourceFile::_ReadInfoTable()
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();
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);
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);
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;
1046 resourceIndex++;
1048 // end of the info block
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.");
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.");
1062 data = skip_bytes(data, kResourceInfoSeparatorSize);
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);
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--;
1082 // _ReadInfoTableEnd
1083 bool
1084 ResourceFile::_ReadInfoTableEnd(const void* data, int32 dataSize)
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);
1109 if (!hasTableEnd)
1110 Warnings::AddCurrentWarning("resource info table has no check sum.");
1111 return hasTableEnd;
1114 // _ReadResourceInfo
1115 const void*
1116 ResourceFile::_ReadResourceInfo(const MemArea& area, const resource_info* info,
1117 type_code type, bool* readIndices)
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;
1131 if (!ignore) {
1132 if (readIndices[index - 1]) {
1133 throw Exception("Multiple resource infos with the same index "
1134 "field: %ld.", index);
1136 readIndices[index - 1] = true;
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);
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);
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);
1159 return skip_bytes(name, nameSize);