Rev 26 | Rev 31 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 26 | Rev 30 | ||
|---|---|---|---|
| Line 134... | Line 134... | ||
| 134 | static uint32_t image_align = 4; // default image alignment, as per QNX docs |
134 | static uint32_t image_align = 4; // default image alignment, as per QNX docs |
| 135 | static uint32_t image_kernel_ino = 0; |
135 | static uint32_t image_kernel_ino = 0; |
| 136 | static uint32_t image_bootscript_ino = 0; |
136 | static uint32_t image_bootscript_ino = 0; |
| 137 | static int startup_header_compression_flag = STARTUP_HDR_FLAGS1_COMPRESS_NONE; |
137 | static int startup_header_compression_flag = STARTUP_HDR_FLAGS1_COMPRESS_NONE; |
| 138 | #if defined(__x86_64__) |
138 | #if defined(__x86_64__) |
| 139 | static char |
139 | static char *image_processor = "x86_64"; // default CPU type for which this image is built, either "x86_64" or "aarch64le" (will be used to find out the right include paths under $QNX_TARGET) |
| 140 | static char |
140 | static char *image_processor_base = "x86_64"; // default base CPU type for which this image is built, either "x86_64" or "aarch64le" (will be used to find out the right include paths under $QNX_TARGET) |
| - | 141 | static size_t image_pagesize = 4 * 1024; // default page size for the image, depends on the CPU type. Intel has 4kb pages, ARM has 16kb ones. |
|
| 141 | #elif defined(__aarch64__) |
142 | #elif defined(__aarch64__) |
| 142 | static char |
143 | static char *image_processor = "aarch64le"; // default CPU type for which this image is built, either "x86_64" or "aarch64le" (will be used to find out the right include paths under $QNX_TARGET) |
| 143 | static char |
144 | static char *image_processor_base = "aarch64"; // default base CPU type for which this image is built, either "x86_64" or "aarch64le" (will be used to find out the right include paths under $QNX_TARGET) |
| - | 145 | static size_t image_pagesize = 16 * 1024; // default page size for the image, depends on the CPU type. Intel has 4kb pages, ARM has 16kb ones. |
|
| 144 | #else // unknown platform |
146 | #else // unknown platform |
| 145 | #error Please port ifstool to this platform |
147 | #error Please port ifstool to this platform |
| 146 | #endif |
148 | #endif |
| 147 | static char *buildfile_pathname = NULL; // pathname of IFS build file |
149 | static char *buildfile_pathname = NULL; // pathname of IFS build file |
| 148 | static char *current_line = NULL; // copy of current line in IFS build file |
150 | static char *current_line = NULL; // copy of current line in IFS build file |
| Line 156... | Line 158... | ||
| 156 | // bootable IFS support |
158 | // bootable IFS support |
| 157 | static char *bootfile_pathname = NULL; // FIXME: HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS |
159 | static char *bootfile_pathname = NULL; // FIXME: HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS |
| 158 | static size_t bootfile_size = 0; // FIXME: HACK: size of the bootcode binary blob file to put at the start of a bootable IFS |
160 | static size_t bootfile_size = 0; // FIXME: HACK: size of the bootcode binary blob file to put at the start of a bootable IFS |
| 159 | static char *startupfile_pathname = NULL; // FIXME: HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS |
161 | static char *startupfile_pathname = NULL; // FIXME: HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS |
| 160 | static size_t startupfile_ep_from_imagebase = 0; // FIXME: HACK: startup code entrypoint offset from image base for a bootable IFS |
162 | static size_t startupfile_ep_from_imagebase = 0; // FIXME: HACK: startup code entrypoint offset from image base for a bootable IFS |
| 161 | static size_t kernelfile_offset = |
163 | static size_t kernelfile_offset = 0; // kernel file offset in the IFS (first offset rounded at pagesize after the dirents table) |
| 162 | 164 | ||
| 163 | 165 | ||
| 164 | // exported function prototypes |
166 | // exported function prototypes |
| 165 | int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness); // compute an IFS image or startup checksum to store in the trailer |
167 | int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness); // compute an IFS image or startup checksum to store in the trailer |
| 166 | 168 | ||
| Line 635... | Line 637... | ||
| 635 | size_t size_in_file; |
637 | size_t size_in_file; |
| 636 | size_t file_offset; |
638 | size_t file_offset; |
| 637 | size_t array_index; |
639 | size_t array_index; |
| 638 | size_t table_index; |
640 | size_t table_index; |
| 639 | size_t table_count; |
641 | size_t table_count; |
| 640 | size_t page_size; |
- | |
| 641 | - | ||
| 642 | // find out the platform page size |
- | |
| 643 | if (ELF_GET_NUMERIC (ELFHDR, ELFHDR, instruction_set) == ELF_MACHINE_X86_64) |
- | |
| 644 | page_size = 4 * 1024; // 4 kb pages on Intel processors |
- | |
| 645 | else if (ELF_GET_NUMERIC (ELFHDR, ELFHDR, instruction_set) == ELF_MACHINE_AARCH64) |
- | |
| 646 | page_size = 16 * 1024; // 16 kb pages on ARM64 |
- | |
| 647 | else |
- | |
| 648 | { |
- | |
| 649 | errno = ENOTSUP; // unsupported architecture: set errno to something meaningful |
- | |
| 650 | return (0); // and return an error value |
- | |
| 651 | } |
- | |
| 652 | 642 | ||
| 653 | // if we should align the segment sizes in the ELF file with their occupied memory size (such is the case for e.g. procnto), do that first |
643 | // if we should align the segment sizes in the ELF file with their occupied memory size (such is the case for e.g. procnto), do that first |
| 654 | table_count = ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_len); // get the number of program headers |
644 | table_count = ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_len); // get the number of program headers |
| 655 | for (table_index = 0; table_index < table_count; table_index++) |
645 | for (table_index = 0; table_index < table_count; table_index++) |
| 656 | { |
646 | { |
| Line 703... | Line 693... | ||
| 703 | if (ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory) > new_shdrtable_offset_method2) |
693 | if (ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory) > new_shdrtable_offset_method2) |
| 704 | new_shdrtable_offset_method2 = ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory); |
694 | new_shdrtable_offset_method2 = ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory); |
| 705 | } |
695 | } |
| 706 | if (new_shdrtable_offset_method2 > new_shdrtable_offset) |
696 | if (new_shdrtable_offset_method2 > new_shdrtable_offset) |
| 707 | LOG_DEBUG ("METHOD2: %llx > %llx", new_shdrtable_offset_method2, new_shdrtable_offset);*/ |
697 | LOG_DEBUG ("METHOD2: %llx > %llx", new_shdrtable_offset_method2, new_shdrtable_offset);*/ |
| 708 | //new_shdrtable_offset = ROUND_TO_UPPER_MULTIPLE (new_shdrtable_offset, |
698 | //new_shdrtable_offset = ROUND_TO_UPPER_MULTIPLE (new_shdrtable_offset, image_pagesize); // round to page size |
| 709 | 699 | ||
| 710 | // re-create the section header table |
700 | // re-create the section header table |
| 711 | ADD_SECTION (".shstrtab", &elf_section); // the first section will be the section names strings table |
701 | ADD_SECTION (".shstrtab", &elf_section); // the first section will be the section names strings table |
| 712 | ASSERT_WITH_ERRNO (Buffer_InitWithByteArray (&elf_section->data, "\0")); // initialize an empty section headers strings table |
702 | ASSERT_WITH_ERRNO (Buffer_InitWithByteArray (&elf_section->data, "\0")); // initialize an empty section headers strings table |
| 713 | ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&elf_section->data, ".shstrtab\0")); // append ".shstrtab" *INCLUDING* its null terminator |
703 | ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&elf_section->data, ".shstrtab\0")); // append ".shstrtab" *INCLUDING* its null terminator |
| Line 766... | Line 756... | ||
| 766 | ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_table_offset, new_shdrtable_offset); |
756 | ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_table_offset, new_shdrtable_offset); |
| 767 | ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_table_len, new_shdrtable_len); |
757 | ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_table_len, new_shdrtable_len); |
| 768 | ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_names_idx, elf_section_count); // the section headers strings table is the last section |
758 | ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_names_idx, elf_section_count); // the section headers strings table is the last section |
| 769 | 759 | ||
| 770 | // align size with page size (4096 on x86, 16k on ARM), zerofilling the extra space |
760 | // align size with page size (4096 on x86, 16k on ARM), zerofilling the extra space |
| 771 | ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (file, ROUND_TO_UPPER_MULTIPLE (file->size, |
761 | ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (file, ROUND_TO_UPPER_MULTIPLE (file->size, image_pagesize))); |
| 772 | 762 | ||
| 773 | // cleanup |
763 | // cleanup |
| 774 | for (table_index = 0; table_index < elf_section_count; table_index++) |
764 | for (table_index = 0; table_index < elf_section_count; table_index++) |
| 775 | Buffer_Forget (&elf_sections[table_index].data); // free all sections' backing buffers |
765 | Buffer_Forget (&elf_sections[table_index].data); // free all sections' backing buffers |
| 776 | 766 | ||
| Line 1757... | Line 1747... | ||
| 1757 | if ((bootfile_pathname == NULL) || (startupfile_pathname == NULL)) // FIXME: HACK until I figure out how to re-create them |
1747 | if ((bootfile_pathname == NULL) || (startupfile_pathname == NULL)) // FIXME: HACK until I figure out how to re-create them |
| 1758 | DIE_WITH_EXITCODE (1, "creating bootable images require the --bootfile and --startupfile command-line options in \"%s\" line %d", buildfile_pathname, lineno); |
1748 | DIE_WITH_EXITCODE (1, "creating bootable images require the --bootfile and --startupfile command-line options in \"%s\" line %d", buildfile_pathname, lineno); |
| 1759 | if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ? |
1749 | if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ? |
| 1760 | { |
1750 | { |
| 1761 | *sep = 0; |
1751 | *sep = 0; |
| - | 1752 | if (strcmp (value, "x86_64") == 0) |
|
| - | 1753 | { |
|
| - | 1754 | image_processor = "x86_64"; // save processor |
|
| - | 1755 | image_processor_base = "x86_64"; // save processor base |
|
| - | 1756 | image_pagesize = 4 * 1024; // Intel processors use 4 Kb pages |
|
| - | 1757 | } |
|
| - | 1758 | else if (strcmp (value, "aarch64le") == 0) |
|
| - | 1759 | { |
|
| 1762 |
|
1760 | image_processor = "aarch64le"; // save processor |
| - | 1761 | image_processor_base = "aarch64"; // save processor base |
|
| - | 1762 | image_pagesize = 16 * 1024; // ARM processors use 16 Kb pages |
|
| - | 1763 | } |
|
| - | 1764 | else |
|
| - | 1765 | DIE_WITH_EXITCODE (1, "unrecognized processor type in 'virtual' attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, value); |
|
| 1763 | value = sep + 1; |
1766 | value = sep + 1; |
| 1764 | } |
1767 | } |
| 1765 | if (stat (bootfile_pathname, &stat_buf) != 0) |
1768 | if (stat (bootfile_pathname, &stat_buf) != 0) |
| 1766 | DIE_WITH_EXITCODE (1, "unable to stat the boot file \"%s\" specified in \"%s\" line %d: %s", bootfile_pathname, buildfile_pathname, lineno, strerror (errno)); |
1769 | DIE_WITH_EXITCODE (1, "unable to stat the boot file \"%s\" specified in \"%s\" line %d: %s", bootfile_pathname, buildfile_pathname, lineno, strerror (errno)); |
| 1767 | bootfile_size = stat_buf.st_size; // save preboot file size |
1770 | bootfile_size = stat_buf.st_size; // save preboot file size |
| Line 1905... | Line 1908... | ||
| 1905 | else |
1908 | else |
| 1906 | { |
1909 | { |
| 1907 | is_escaped_char = false; // any other char, meaning the next one will not be escaped |
1910 | is_escaped_char = false; // any other char, meaning the next one will not be escaped |
| 1908 | if (!should_discard_inline_contents) // only store the contents if we do NOT know the data yet |
1911 | if (!should_discard_inline_contents) // only store the contents if we do NOT know the data yet |
| 1909 | { |
1912 | { |
| 1910 | if (entry_parms.data.size == allocated_size) // reallocate in |
1913 | if (entry_parms.data.size == allocated_size) // reallocate in 16k blocks |
| 1911 | { |
1914 | { |
| 1912 | reallocated_ptr = realloc (entry_parms.data.bytes, allocated_size + |
1915 | reallocated_ptr = realloc (entry_parms.data.bytes, allocated_size + 16384); |
| 1913 | ASSERT_WITH_ERRNO (reallocated_ptr); |
1916 | ASSERT_WITH_ERRNO (reallocated_ptr); |
| 1914 | entry_parms.data.bytes = reallocated_ptr; |
1917 | entry_parms.data.bytes = reallocated_ptr; |
| 1915 | allocated_size += |
1918 | allocated_size += 16384; |
| 1916 | } |
1919 | } |
| 1917 | entry_parms.data.bytes[entry_parms.data.size++] = read_char; |
1920 | entry_parms.data.bytes[entry_parms.data.size++] = read_char; |
| 1918 | } |
1921 | } |
| 1919 | if (read_char == '\n') |
1922 | if (read_char == '\n') |
| 1920 | lineno++; // update line counter as we parse the inline content |
1923 | lineno++; // update line counter as we parse the inline content |
| Line 2392... | Line 2395... | ||
| 2392 | imgdir_size = ifs.data.size - ifs.offsets.imagedir; // measure image dir size and save it for future use |
2395 | imgdir_size = ifs.data.size - ifs.offsets.imagedir; // measure image dir size and save it for future use |
| 2393 | 2396 | ||
| 2394 | // is it a bootable image with a startup file ? |
2397 | // is it a bootable image with a startup file ? |
| 2395 | if (startupfile_pathname != NULL) |
2398 | if (startupfile_pathname != NULL) |
| 2396 | { |
2399 | { |
| - | 2400 | // compute the kernel offset: address of the first page that comes after the directory entries |
|
| - | 2401 | kernelfile_offset = ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_pagesize); |
|
| - | 2402 | ||
| 2397 | // write the filesystem entries that may fit before the kernel |
2403 | // write the filesystem entries that may fit before the kernel |
| 2398 | for (;;) |
2404 | for (;;) |
| 2399 | { |
2405 | { |
| 2400 | available_space = kernelfile_offset - ifs.data.size; // measure the available space until the kernel |
2406 | available_space = kernelfile_offset - ifs.data.size; // measure the available space until the kernel |
| 2401 | 2407 | ||
| Line 2541... | Line 2547... | ||
| 2541 | if (startup_header_compression_flag == STARTUP_HDR_FLAGS1_COMPRESS_UCL) |
2547 | if (startup_header_compression_flag == STARTUP_HDR_FLAGS1_COMPRESS_UCL) |
| 2542 | { |
2548 | { |
| 2543 | // UCL compression. NOTE: the decompressor function used in startup-x86 is "ucl_nrv2b_decompress_8". Also compression level is hardcoded at 9 in mkifs, but can range from 1 to 10 |
2549 | // UCL compression. NOTE: the decompressor function used in startup-x86 is "ucl_nrv2b_decompress_8". Also compression level is hardcoded at 9 in mkifs, but can range from 1 to 10 |
| 2544 | static ucl_uint ucl_outlen; // have a different variable because of pointer size mismatch |
2550 | static ucl_uint ucl_outlen; // have a different variable because of pointer size mismatch |
| 2545 | while (((compressor_ret = ucl_nrv2b_99_compress (compressor_in, (ucl_uint) compressor_inlen, compressor_out, &ucl_outlen, NULL, 9, NULL, NULL)) == UCL_E_OK) && (ucl_outlen > 0xFFFF)) |
2551 | while (((compressor_ret = ucl_nrv2b_99_compress (compressor_in, (ucl_uint) compressor_inlen, compressor_out, &ucl_outlen, NULL, 9, NULL, NULL)) == UCL_E_OK) && (ucl_outlen > 0xFFFF)) |
| 2546 | compressor_inlen -= |
2552 | compressor_inlen -= image_pagesize; // as long as we can't produce compressed blocks whose size can fit in 2 bytes, try again with one page input less |
| 2547 | ASSERT (compressor_ret == UCL_E_OK, "UCL compression error: ucl_nrv2b_99_compress() returned %d", compressor_ret); // make sure it's not a compression error |
2553 | ASSERT (compressor_ret == UCL_E_OK, "UCL compression error: ucl_nrv2b_99_compress() returned %d", compressor_ret); // make sure it's not a compression error |
| 2548 | compressor_outlen = ucl_outlen; // cast back to size_t |
2554 | compressor_outlen = ucl_outlen; // cast back to size_t |
| 2549 | } |
2555 | } |
| 2550 | else if (startup_header_compression_flag == STARTUP_HDR_FLAGS1_COMPRESS_LZO) |
2556 | else if (startup_header_compression_flag == STARTUP_HDR_FLAGS1_COMPRESS_LZO) |
| 2551 | { |
2557 | { |
| 2552 | // LZO compression. NOTE: the compressor function used in mkifs is "lzo1x_999_compress" which is from the full LZO package... I use use minilzo, but I have to admit the compression ratio of the full LZO is *much* better. |
2558 | // LZO compression. NOTE: the compressor function used in mkifs is "lzo1x_999_compress" which is from the full LZO package... I use use minilzo, but I have to admit the compression ratio of the full LZO is *much* better. |
| 2553 | static lzo_align_t lzo_workmem[(LZO1X_1_MEM_COMPRESS + (sizeof (lzo_align_t) - 1)) / sizeof(lzo_align_t)]; // heap-allocated aligned buffer |
2559 | static lzo_align_t lzo_workmem[(LZO1X_1_MEM_COMPRESS + (sizeof (lzo_align_t) - 1)) / sizeof(lzo_align_t)]; // heap-allocated aligned buffer |
| 2554 | static lzo_uint lzo_outlen; // have a different variable because of pointer size mismatch |
2560 | static lzo_uint lzo_outlen; // have a different variable because of pointer size mismatch |
| 2555 | while (((compressor_ret = lzo1x_1_compress (compressor_in, compressor_inlen, compressor_out, &lzo_outlen, lzo_workmem)) == UCL_E_OK) && (lzo_outlen > 0xFFFF)) |
2561 | while (((compressor_ret = lzo1x_1_compress (compressor_in, compressor_inlen, compressor_out, &lzo_outlen, lzo_workmem)) == UCL_E_OK) && (lzo_outlen > 0xFFFF)) |
| 2556 | compressor_inlen -= |
2562 | compressor_inlen -= image_pagesize; // as long as we can't produce compressed blocks whose size can fit in 2 bytes, try again with one page input less |
| 2557 | ASSERT (compressor_ret == LZO_E_OK, "LZO compression error: lzo1x_1_compress() returned %d", compressor_ret); // make sure it's not a compression error |
2563 | ASSERT (compressor_ret == LZO_E_OK, "LZO compression error: lzo1x_1_compress() returned %d", compressor_ret); // make sure it's not a compression error |
| 2558 | compressor_outlen = lzo_outlen; // cast back to size_t |
2564 | compressor_outlen = lzo_outlen; // cast back to size_t |
| 2559 | } |
2565 | } |
| 2560 | else if (startup_header_compression_flag == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB) |
2566 | else if (startup_header_compression_flag == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB) |
| 2561 | { |
2567 | { |
| 2562 | // Zlib (TODO) |
2568 | // Zlib (TODO) |
| 2563 | } |
2569 | } |
| 2564 | 2570 | ||
| 2565 | // the compression produced a block smaller than 65536 bytes |
2571 | // the compression produced a block smaller than 65536 bytes |
| 2566 | LOG_DEBUG |
2572 | //LOG_DEBUG ("compressed block size %zd", compressor_outlen); |
| 2567 | compressed_imagefs.bytes[compressed_imagefs.size + 0] = (uint8_t) (compressor_outlen >> 8); // write compressed block size word (in big endian) |
2573 | compressed_imagefs.bytes[compressed_imagefs.size + 0] = (uint8_t) (compressor_outlen >> 8); // write compressed block size word (in big endian) |
| 2568 | compressed_imagefs.bytes[compressed_imagefs.size + 1] = (uint8_t) (compressor_outlen >> 0); |
2574 | compressed_imagefs.bytes[compressed_imagefs.size + 1] = (uint8_t) (compressor_outlen >> 0); |
| 2569 | compressed_imagefs.size += 2 + (size_t) compressor_outlen; // advance in compression buffer by the compressed block size word plus the compressed block size |
2575 | compressed_imagefs.size += 2 + (size_t) compressor_outlen; // advance in compression buffer by the compressed block size word plus the compressed block size |
| 2570 | 2576 | ||
| 2571 | remaining_len -= compressor_inlen; // see how many bytes remain to compress |
2577 | remaining_len -= compressor_inlen; // see how many bytes remain to compress |