Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 9 | Rev 11 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. // ifstool.c -- portable reimplementation of QNX's mkifs by Pierre-Marie Baty <pm@pmbaty.com>
  2.  
  3. #include <stdint.h>
  4. #include <stdbool.h>
  5. #include <stdlib.h>
  6. #include <stdarg.h>
  7. #include <stdio.h>
  8. #include <string.h>
  9. #include <errno.h>
  10. #include <sys/stat.h>
  11. #include <ctype.h>
  12. #include <time.h>
  13.  
  14.  
  15. #ifdef _MSC_VER
  16. #include <io.h>
  17. #define __x86_64__ 1
  18. #define __ORDER_BIG_ENDIAN__    4321
  19. #define __ORDER_LITTLE_ENDIAN__ 1234
  20. #define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
  21. #define __attribute__(x)
  22. #define __builtin_bswap16(x) _byteswap_ushort ((unsigned short) (x))
  23. #define __builtin_bswap32(x) _byteswap_ulong ((unsigned long) (x))
  24. #define __builtin_bswap64(x) _byteswap_uint64 ((unsigned long long) (x))
  25. #define S_IFIFO 0x1000
  26. #define S_IFLNK 0xa000
  27. #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
  28. #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
  29. #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
  30. #define strdup(s) _strdup ((s))
  31. #define strcasecmp(s1,s2) _stricmp ((s1), (s2))
  32. #define fseek(fp,off,m) _fseeki64 ((fp), (off), (m))
  33. #define access(p,m) _access ((p), (m))
  34. #define MAXPATHLEN 1024
  35. #ifndef thread_local
  36. #define thread_local __declspec(thread) // the thread_local keyword wasn't defined before C++11 and C23
  37. #endif // !thread_local
  38. #else // !_MSC_VER
  39. #include <sys/param.h>
  40. #include <unistd.h>
  41. #ifndef thread_local
  42. #define thread_local __thread // the thread_local keyword wasn't defined before C++11 and C23
  43. #endif // !thread_local
  44. #endif // _MSC_VER
  45.  
  46.  
  47. // handy macros that generate a version number in the format "YYYYMMDD" corresponding to the build date. Usage: printf ("version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
  48. #ifndef VERSION_ARG_YYYYMMDD
  49. #define BUILDDATE_YEAR  (&__DATE__[7]) // compiler will optimize this into a const string, e.g. "2021"
  50. #define BUILDDATE_MONTH (*((uint32_t *) __DATE__) == *((uint32_t *) "Jan ") ? "01" : \
  51.                          (*((uint32_t *) __DATE__) == *((uint32_t *) "Feb ") ? "02" : \
  52.                           (*((uint32_t *) __DATE__) == *((uint32_t *) "Mar ") ? "03" : \
  53.                            (*((uint32_t *) __DATE__) == *((uint32_t *) "Apr ") ? "04" : \
  54.                             (*((uint32_t *) __DATE__) == *((uint32_t *) "May ") ? "05" : \
  55.                              (*((uint32_t *) __DATE__) == *((uint32_t *) "Jun ") ? "06" : \
  56.                               (*((uint32_t *) __DATE__) == *((uint32_t *) "Jul ") ? "07" : \
  57.                                (*((uint32_t *) __DATE__) == *((uint32_t *) "Aug ") ? "08" : \
  58.                                 (*((uint32_t *) __DATE__) == *((uint32_t *) "Sep ") ? "09" : \
  59.                                  (*((uint32_t *) __DATE__) == *((uint32_t *) "Oct ") ? "10" : \
  60.                                   (*((uint32_t *) __DATE__) == *((uint32_t *) "Nov ") ? "11" : \
  61.                                    (*((uint32_t *) __DATE__) == *((uint32_t *) "Dec ") ? "12" : "XX")))))))))))) // compiler will optimize this into a const string, e.g. "11"
  62. #define BUILDDATE_DAY   (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  1 ") ? "01" : \
  63.                          (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  2 ") ? "02" : \
  64.                           (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  3 ") ? "03" : \
  65.                            (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  4 ") ? "04" : \
  66.                             (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  5 ") ? "05" : \
  67.                              (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  6 ") ? "06" : \
  68.                               (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  7 ") ? "07" : \
  69.                                (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  8 ") ? "08" : \
  70.                                 (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) "  9 ") ? "09" : \
  71.                                  (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 10 ") ? "10" : \
  72.                                   (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 11 ") ? "11" : \
  73.                                    (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 12 ") ? "12" : \
  74.                                     (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 13 ") ? "13" : \
  75.                                      (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 14 ") ? "14" : \
  76.                                       (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 15 ") ? "15" : \
  77.                                        (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 16 ") ? "16" : \
  78.                                         (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 17 ") ? "17" : \
  79.                                          (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 18 ") ? "18" : \
  80.                                           (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 19 ") ? "19" : \
  81.                                            (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 20 ") ? "20" : \
  82.                                             (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 21 ") ? "21" : \
  83.                                              (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 22 ") ? "22" : \
  84.                                               (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 23 ") ? "23" : \
  85.                                                (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 24 ") ? "24" : \
  86.                                                 (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 25 ") ? "25" : \
  87.                                                  (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 26 ") ? "26" : \
  88.                                                   (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 27 ") ? "27" : \
  89.                                                    (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 28 ") ? "28" : \
  90.                                                     (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 29 ") ? "29" : \
  91.                                                      (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 30 ") ? "30" : \
  92.                                                       (*((uint32_t *) &__DATE__[3]) == *((uint32_t *) " 31 ") ? "31" : "XX"))))))))))))))))))))))))))))))) // compiler will optimize this into a const string, e.g. "14"
  93. #define VERSION_FMT_YYYYMMDD "%04s%02s%02s"
  94. #define VERSION_ARG_YYYYMMDD BUILDDATE_YEAR, BUILDDATE_MONTH, BUILDDATE_DAY
  95. #endif // !VERSION_ARG_YYYYMMDD
  96.  
  97.  
  98. // we don't mind about this macro's efficiency...
  99. #define __FILENAME__ (strrchr (__FILE__, '\\') ? strrchr (__FILE__, '\\') + 1 : (strrchr (__FILE__, '/') ? strrchr (__FILE__, '/') + 1 : __FILE__))
  100.  
  101.  
  102. // exit less brutally than with abort() if something doesn't go the way we'd like to
  103. #define WELLMANNERED_ASSERT(is_is_true,...) do { if (!(is_is_true)) { fprintf (stderr, "ifstool: fatal error: assertion within %s() in %s line %d failed: ", __FUNCTION__, __FILENAME__, __LINE__); fprintf (stderr, __VA_ARGS__); fputc ('\n', stderr); exit (1); } } while (0)
  104.  
  105.  
  106. // checked read/write/seek operations
  107. #define fseek_or_die(fp,pos,mode) do { if (fseek ((fp), (pos), (mode)) != 0) { fprintf (stderr, "ifstool: fatal error: fseek() failure within %s() in %s line %d: errno %d (%s)\n", __FUNCTION__, __FILENAME__, __LINE__, errno, strerror (errno)); exit (1); } } while (0)
  108. #define fread_or_die(buf,sz,len,fp) do { if (fread ((buf), (sz), (len), (fp)) != (len)) { fprintf (stderr, "ifstool: fatal error: fread() failure within %s() in %s line %d: errno %d (%s)\n", __FUNCTION__, __FILENAME__, __LINE__, errno, strerror (errno)); exit (1); } } while (0)
  109. #define fwrite_or_die(buf,sz,len,fp) do { if ((fwrite ((buf), (sz), (len), (fp)) != (len)) || (fflush ((fp)) != 0)) { fprintf (stderr, "ifstool: fatal error: fwrite() failure within %s() in %s line %d: errno %d (%s)\n", __FUNCTION__, __FILENAME__, __LINE__, errno, strerror (errno)); exit (1); } } while (0)
  110.  
  111.  
  112. #define ROUND_TO_UPPER_MULTIPLE(val,multiple) ((((val) + (size_t) (multiple) - 1) / (multiple)) * (multiple)) // note that val is being evaluated once, so it can be the result of a function call
  113. #ifdef _WIN32
  114. #define IS_DIRSEP(c) (((c) == '/') || ((c) == '\\'))
  115. #define PATH_SEP ';'
  116. #define PATH_SEP_STR ";"
  117. #else // !_WIN32, thus POSIX
  118. #define IS_DIRSEP(c) ((c) == '/')
  119. #define PATH_SEP ':'
  120. #define PATH_SEP_STR ":"
  121. #endif // _WIN32
  122. #define RECORD_SEP '\x1e' // ASCII record separator
  123. #define RECORD_SEP_STR "\x1e" // ASCII record separator (as string)
  124.  
  125. // macros for accessing ELF files
  126. #define ELF_MAGIC_STR "\x7f" "ELF"
  127. #define ELF_ENDIAN_LITTLE 1 // 'endianness' member of an ELF header: ELF file is little endian
  128. #define ELF_ENDIAN_BIG    2 // 'endianness' member of an ELF header: ELF file is big endian
  129. #define ELF_MACHINE_X86_64  0x3e // 'instruction_set' member of an ELF header, also used in the IFS startup header: ELF file is for x86_64 processors (62 decimal)
  130. #define ELF_MACHINE_AARCH64 0xb7 // 'instruction_set' member of an ELF header, also used in the IFS startup header: ELF file is for ARM64 processors (183 decimal)
  131. #define ELF_SECTIONTYPE_STRINGTABLE 3
  132. #define ELF_DT_NULL    0 // marks end of dynamic section
  133. #define ELF_DT_SONAME 14 // canonical name of shared object
  134. #define ELF_GET_NUMERIC(elfhdr,elfstruct,member) ((elfhdr)->u.elf.platform_size == 2 ? /* is it a 64-bit ELF file ? */ \
  135.    ( \
  136.       (sizeof ((elfstruct)->u.elf64.member) == 1) || (((elfhdr)->u.elf.endianness == 1) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || (((elfhdr)->u.elf.endianness == 2) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) ? /* single-byte, or same endianness ? */ \
  137.          (elfstruct)->u.elf64.member /* same endianness, or single byte required: don't swap */ \
  138.       : /* else */ \
  139.          (sizeof ((elfstruct)->u.elf64.member) == 8 ? __builtin_bswap64 ((elfstruct)->u.elf64.member) : (sizeof ((elfstruct)->u.elf64.member) == 4 ? __builtin_bswap32 ((elfstruct)->u.elf64.member) : __builtin_bswap16 ((elfstruct)->u.elf64.member))) /* different endianness: swap */ \
  140.    ) \
  141.    : /* else peek at 32-bit ELF */ \
  142.    ( \
  143.       (sizeof ((elfstruct)->u.elf32.member) == 1) || (((elfhdr)->u.elf.endianness == 1) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || (((elfhdr)->u.elf.endianness == 2) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) ? /* single-byte, or same endianness ? */ \
  144.          (elfstruct)->u.elf32.member /* same endianness, or single byte required: don't swap */ \
  145.       : /* else */ \
  146.          (sizeof ((elfstruct)->u.elf32.member) == 4 ? __builtin_bswap32 ((elfstruct)->u.elf32.member) : __builtin_bswap16 ((elfstruct)->u.elf32.member)) /* different endianness: swap */ \
  147.    ) \
  148. ) // this macro supports 32- and 64-bit ELF files in low and big endianness transparently
  149. #define ELF_SET_NUMERIC(elfhdr,elfstruct,member,data) ((elfhdr)->u.elf.platform_size == 2 ? /* is it a 64-bit ELF file ? */ \
  150.    ((elfstruct)->u.elf64.member = ( \
  151.       (sizeof ((elfstruct)->u.elf64.member) == 1) || (((elfhdr)->u.elf.endianness == 1) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || (((elfhdr)->u.elf.endianness == 2) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) ? /* single-byte, or same endianness ? */ \
  152.          (sizeof ((elfstruct)->u.elf64.member) == 8 ? (uint64_t) ((data)) : (sizeof ((elfstruct)->u.elf64.member) == 4 ? (uint32_t) ((data)) : (sizeof ((elfstruct)->u.elf64.member) == 2 ? (uint16_t) ((data)) : (uint8_t) ((data))))) /* same endianness, or single byte required: don't swap */ \
  153.       : /* else */ \
  154.          (sizeof ((elfstruct)->u.elf64.member) == 8 ? __builtin_bswap64 ((data)) : (sizeof ((elfstruct)->u.elf64.member) == 4 ? __builtin_bswap32 ((data)) : __builtin_bswap16 ((data)))) /* different endianness: swap */ \
  155.    )) \
  156.    : /* else poke at 32-bit ELF */ \
  157.    ((elfstruct)->u.elf32.member = ( \
  158.       (sizeof ((elfstruct)->u.elf32.member) == 1) || (((elfhdr)->u.elf.endianness == 1) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || (((elfhdr)->u.elf.endianness == 2) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) ? /* single-byte, or same endianness ? */ \
  159.          (sizeof ((elfstruct)->u.elf64.member) == 4 ? (uint32_t) ((data)) : (sizeof ((elfstruct)->u.elf64.member) == 2 ? (uint16_t) ((data)) : (uint8_t) ((data)))) /* same endianness, or single byte required: don't swap */ \
  160.       : /* else */ \
  161.          (sizeof ((elfstruct)->u.elf32.member) == 4 ? __builtin_bswap32 ((data)) : __builtin_bswap16 ((data))) /* different endianness: swap */ \
  162.    )) \
  163. ) // this macro supports 32- and 64-bit ELF files in low and big endianness transparently
  164. #define ELF_GET_STRING(elfhdr,elfstruct,member) ((elfhdr)->u.elf.platform_size == 2 ? (elfstruct)->u.elf64.member : (elfstruct)->u.elf32.member) // this macro supports 32- and 64-bit ELF files transparently
  165. #define ELF_SET_STRING(elfhdr,elfstruct,member,data,len) memcpy (((elfhdr)->u.elf.platform_size == 2 ? (elfstruct)->u.elf64.member : (elfstruct)->u.elf32.member), (data), (len)) // this macro supports 32- and 64-bit ELF files transparently
  166. #define ELF_STRUCT_SIZE(elfhdr,elfstruct) ((elfhdr)->u.elf.platform_size == 2 ? sizeof ((elfstruct)->u.elf64) : sizeof ((elfstruct)->u.elf32)) // this macro supports 32- and 64-bit ELF files transparently
  167.  
  168.  
  169.  
  170. #define WILL_BE_FILLED_LATER 0xbaadf00d
  171.  
  172.  
  173. // bitmapped flags used in the flags1 member of the startup header
  174. #define STARTUP_HDR_FLAGS1_VIRTUAL        (1 << 0)
  175. #define STARTUP_HDR_FLAGS1_BIGENDIAN      (1 << 1)
  176. //#define STARTUP_HDR_FLAGS1_COMPRESS_MASK  0x1c
  177. //#define STARTUP_HDR_FLAGS1_COMPRESS_SHIFT 0x02
  178. //#define STARTUP_HDR_FLAGS1_COMPRESS_NONE  0x00
  179. //#define STARTUP_HDR_FLAGS1_COMPRESS_ZLIB  0x04
  180. //#define STARTUP_HDR_FLAGS1_COMPRESS_LZO   0x08
  181. //#define STARTUP_HDR_FLAGS1_COMPRESS_UCL   0x0c
  182. #define STARTUP_HDR_FLAGS1_TRAILER_V2     (1 << 5) // if set, then a struct startup_trailer_v2 follows the startup. If the image is compressed, then the compressed imagefs is followed by a struct image_trailer_v2
  183.  
  184.  
  185. // bitmapped flags used in the flags member of the image header
  186. #define IMAGE_FLAGS_BIGENDIAN  (1 << 0) // header, trailer, dirents in big-endian format
  187. #define IMAGE_FLAGS_READONLY   (1 << 1) // do not try to write to image (rom/flash)
  188. #define IMAGE_FLAGS_INO_BITS   (1 << 2) // inode bits valid
  189. #define IMAGE_FLAGS_SORTED     (1 << 3) // dirent section is sorted (by pathname)
  190. #define IMAGE_FLAGS_TRAILER_V2 (1 << 4) // image uses struct image_trailer_v2
  191.  
  192.  
  193. // bitmapped flags superposed to a filesystem entry's inode number
  194. #define IFS_INO_PROCESSED_ELF 0x80000000
  195. #define IFS_INO_RUNONCE_ELF   0x40000000
  196. #define IFS_INO_BOOTSTRAP_EXE 0x20000000
  197.  
  198.  
  199. // SHA-512 block and digest sizes
  200. #define SHA512_BLOCK_LENGTH 128 // in bytes
  201. #define SHA512_DIGEST_LENGTH 64 // in bytes
  202.  
  203.  
  204. // SHA-512 computation context structure type definition
  205. typedef struct sha512_ctx_s
  206. {
  207.    uint64_t state[8];
  208.    uint64_t bitcount[2];
  209.    uint8_t buffer[SHA512_BLOCK_LENGTH];
  210. } SHA512_CTX;
  211.  
  212.  
  213. #ifdef _MSC_VER
  214. #pragma pack(push)
  215. #pragma pack(1)
  216. #endif // _MSC_VER
  217.  
  218.  
  219. #if 0 // TODO: startup script compiler. Someday.
  220. #define SCRIPT_FLAGS_EXTSCHED   0x01
  221. #define SCRIPT_FLAGS_SESSION    0x02
  222. #define SCRIPT_FLAGS_SCHED_SET  0x04
  223. #define SCRIPT_FLAGS_CPU_SET    0x08
  224. #define SCRIPT_FLAGS_BACKGROUND 0x20
  225. #define SCRIPT_FLAGS_KDEBUG     0x40
  226.  
  227. #define SCRIPT_POLICY_NOCHANGE 0
  228. #define SCRIPT_POLICY_FIFO     1
  229. #define SCRIPT_POLICY_RR       2
  230. #define SCRIPT_POLICY_OTHER    3
  231.  
  232. #define SCRIPT_TYPE_EXTERNAL        0
  233. #define SCRIPT_TYPE_WAITFOR         1
  234. #define SCRIPT_TYPE_REOPEN          2
  235. #define SCRIPT_TYPE_DISPLAY_MSG     3
  236. #define SCRIPT_TYPE_PROCMGR_SYMLINK 4
  237. #define SCRIPT_TYPE_EXTSCHED_APS    5
  238.  
  239. #define SCRIPT_CHECKS_MS 100
  240.  
  241. #define SCRIPT_SCHED_EXT_NONE 0
  242. #define SCRIPT_SCHED_EXT_APS  1
  243.  
  244. #define SCRIPT_APS_SYSTEM_PARTITION_ID   0
  245. #define SCRIPT_APS_SYSTEM_PARTITION_NAME "System"
  246. #define SCRIPT_APS_PARTITION_NAME_LENGTH 15
  247. #define SCRIPT_APS_MAX_PARTITIONS        8
  248.  
  249.  
  250. typedef struct __attribute__((packed)) bootscriptcmd_header_s
  251. {
  252.    uint16_t size; // size of cmd entry
  253.    uint8_t type;
  254.    uint8_t spare;
  255. } bootscriptcmd_header_t;
  256.  
  257.  
  258. typedef union bootscriptcmd_s
  259. {
  260.    struct __attribute__((packed)) script_external
  261.    {
  262.       bootscriptcmd_header_t hdr;
  263.       uint8_t cpu; // CPU (turn into runmask)
  264.       uint8_t flags;
  265.       union script_external_extsched
  266.       {
  267.          uint8_t reserved[2];
  268.          struct __attribute__((packed))
  269.          {
  270.             uint8_t id;
  271.             uint8_t reserved[1];
  272.          } aps;
  273.       } extsched; // extended scheduler
  274.       uint8_t policy; // POLICY_FIFO, POLICY_RR, ...
  275.       uint8_t priority; // priority to run cmd at
  276.       uint8_t argc; // # of args
  277.       uint8_t envc; // # of environment entries
  278.       char args[0]; // executable, argv, envp (null padded to 32-bit align)
  279.    } external;
  280.    struct __attribute__((packed)) script_waitfor_reopen
  281.    {
  282.       bootscriptcmd_header_t hdr;
  283.       uint16_t checks;
  284.       char fname[0]; // char fname[] (null padded to 32-bit align)
  285.    } waitfor_reopen;
  286.    struct __attribute__((packed)) script_display_msg
  287.    {
  288.       bootscriptcmd_header_t hdr;
  289.       char msg[0]; // char msg[] (null padded to 32-bit align)
  290.    } display_msg;
  291.    struct __attribute__((packed)) script_procmgr_symlink
  292.    {
  293.       bootscriptcmd_header_t hdr;
  294.       char src_dest[0]; // <src_name>, '\0', <dest_name> '\0' (null padded to 32-bit align)
  295.    } procmgr_symlink;
  296.    struct __attribute__((packed)) script_extsched_aps
  297.    {
  298.       bootscriptcmd_header_t hdr;
  299.       uint8_t parent;
  300.       uint8_t budget;
  301.       uint16_t critical;
  302.       uint8_t id;
  303.       char pname[0]; // char pname[] (null padded to 32-bit align)
  304.    } extsched_aps;
  305. } bootscriptcmd_t;
  306. #endif // 0
  307.  
  308.  
  309. #define INITIAL_STARTUP_SCRIPT \
  310.    /* procmgr_symlink /proc/boot/ldqnx-64.so.2 /usr/lib/ldqnx-64.so.2 */ \
  311.    "\x34\x00" /*size*/ "\x04" /*type*/ "\x00" /*spare*/ "/proc/boot/ldqnx-64.so.2\0" "/usr/lib/ldqnx-64.so.2\0" \
  312.    /* sh /proc/boot/startup.sh */ \
  313.    "\x88\x00" /*size*/ "\x00" /*type*/ "\x00" /*spare*/ "\x00" /*CPU mask*/ "\x00" /*flags*/ "\x00\x00" /*reserved*/ "\x00" /*policy*/ "\x00" /*priority*/ "\02" /*argc*/ "\x02" /*envc*/ "sh\0" /*executable*/ "sh\0" "/proc/boot/startup.sh\0" /*argv*/ "PATH=/sbin:/usr/sbin:/bin:/usr/bin:/proc/boot\0" "LD_LIBRARY_PATH=/proc/boot:/lib:/lib/dll:/usr/lib\0" /*envp*/ \
  314.    /* display_msg "Startup complete */ \
  315.    "\x18\x00" /*size*/ "\x03" /*type*/ "\x00" /*spare*/ "Startup complete\n\0" "\x00\00" /*padding*/ \
  316.    /* trailer */ \
  317.    "\x00\x00\x00\x00"
  318.  
  319.  
  320. typedef struct __attribute__((packed)) fsentry_s
  321. {
  322.    struct __attribute__((packed)) fsentry_header_s
  323.    {
  324.       uint16_t size; // size of dirent
  325.       uint16_t extattr_offset; // if zero, no extattr data
  326.       uint32_t ino; // if zero, skip entry
  327.       uint32_t mode; // mode and perms of entry
  328.       uint32_t gid;
  329.       uint32_t uid;
  330.       uint32_t mtime;
  331.    } header;
  332.    union __attribute__((packed)) fsentry_specific_u
  333.    {
  334.       struct __attribute__((packed)) fsentry_file_s // when (mode & S_IFMT) == S_IFREG
  335.       {
  336.          uint32_t offset; // offset from header
  337.          uint32_t size;
  338.          char *path; // null terminated path (no leading slash)
  339.          char *UNSAVED_databuf; // file data blob buffer (NOT SAVED IN THE IFS)
  340.       } file;
  341.       struct __attribute__((packed)) fsentry_dir_s // when (mode & S_IFMT) == S_IFDIR
  342.       {
  343.          char *path; // null terminated path (no leading slash)
  344.       } dir;
  345.       struct __attribute__((packed)) fsentry_symlink_s // when (mode & S_IFMT) == S_IFLNK
  346.       {
  347.          uint16_t sym_offset; // offset to 'contents' from 'path'
  348.          uint16_t sym_size; // strlen (contents)
  349.          char *path; // null terminated path (no leading slash)
  350.          char *contents; // null terminated symlink contents
  351.       } symlink;
  352.       struct __attribute__((packed)) fsentry_device_s // when (mode & S_IFMT) == S_IF<CHR|BLK|FIFO|NAM|SOCK>
  353.       {
  354.          uint32_t dev;
  355.          uint32_t rdev;
  356.          char *path; // null terminated path (no leading slash)
  357.       } device;
  358.    } u;
  359.    bool UNSAVED_was_data_written; // whether this entry's data was written to the image (NOT SAVED IN THE IFS)
  360. } fsentry_t;
  361.  
  362.  
  363. typedef struct __attribute__((packed)) startup_header_s // size 256 bytes
  364. {
  365.                            // I - used by the QNX IPL
  366.                            // S - used by the startup program
  367.    uint8_t signature[4];   // [I ] Header signature, "\xeb\x7e\xff\x00"
  368.    uint16_t version;       // [I ] Header version, i.e. 1
  369.    uint8_t flags1;         // [IS] Misc flags, 0x21 (= 0x20 | STARTUP_HDR_FLAGS1_VIRTUAL)
  370.    uint8_t flags2;         // [  ] No flags defined yet (0)
  371.    uint16_t header_size;   // [ S] sizeof(struct startup_header), i.e. 256
  372.    uint16_t machine;       // [IS] Machine type from elfdefinitions.h, i.e. 0x003E --> _ELF_DEFINE_EM(EM_X86_64, 62, "AMD x86-64 architecture")
  373.    uint32_t startup_vaddr; // [I ] Virtual Address to transfer to after IPL is done, here 0x01403008 (appears in "Entry" column for "startup.*")
  374.    uint32_t paddr_bias;    // [ S] Value to add to physical address to get a value to put into a pointer and indirected through, here 0 (no indirections)
  375.    uint32_t image_paddr;   // [IS] Physical address of image, here 0x01400f30 (appears in "Offset" column for "startup-header" which is the first entry/start of file)
  376.    uint32_t ram_paddr;     // [IS] Physical address of RAM to copy image to (startup_size bytes copied), here 0x01400f30 (same as above)
  377.    uint32_t ram_size;      // [ S] Amount of RAM used by the startup program and executables contained in the file system, here 0x00cd6128 i.e. 13 459 752 dec. which is 13 Mb. i.e. IFS file size minus 0x9eee
  378.    uint32_t startup_size;  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
  379.    uint32_t stored_size;   // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
  380.    uint32_t imagefs_paddr; // [IS] Set by IPL to where the imagefs is when startup runs (0)
  381.    uint32_t imagefs_size;  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
  382.    uint16_t preboot_size;  // [I ] Size of loaded before header, here 0xf30 or 3888 bytes (size of "bios.boot" file))
  383.    uint16_t zero0;         // [  ] Zeros
  384.    uint32_t zero[1];       // [  ] Zeros
  385.    uint64_t addr_off;      // [ S] Offset to add to startup_vaddr, image_paddr, ram_paddr, and imagefs_paddr members, here zero (0)
  386.    uint32_t info[48];      // [IS] Array of startup_info* structures (zero filled)
  387. } startup_header_t;
  388.  
  389.  
  390. typedef struct __attribute__((packed)) startup_trailer_s
  391. {
  392.    uint32_t cksum; // checksum from start of header to start of trailer
  393. } startup_trailer_v1_t;
  394.  
  395.  
  396. // NOTE: The checksums in this trailer will only be valid prior to entering startup.
  397. // Because the startup binary is executed in-place, its data segment will change once the program is running.
  398. // Hence, any checksum validation would need to be done by the boot loader / IFS.
  399. typedef struct __attribute__((packed)) startup_trailer_v2_s
  400. {
  401.    uint8_t sha512[64]; // SHA512 from start of header to start of trailer
  402.    uint32_t cksum; // checksum from start of header to start of this member
  403. } startup_trailer_v2_t;
  404.  
  405.  
  406. typedef struct __attribute__((packed)) image_header_s
  407. {
  408.    uint8_t signature[7]; // image filesystem signature, i.e. "imagefs"
  409.    uint8_t flags; // endian neutral flags, 0x1c
  410.    uint32_t image_size; // size from start of header to end of trailer (here 0xca6fe0 or 13 266 912)
  411.    uint32_t hdr_dir_size; // size from start of header to last dirent (here 0x12b8 or 4792)
  412.    uint32_t dir_offset; // offset from start of header to start of first dirent (here 0x5c or 92)
  413.    uint32_t boot_ino[4]; // inode of files for bootstrap pgms (here 0xa0000002, 0, 0, 0)
  414.    uint32_t script_ino; // inode of file for script (here 3)
  415.    uint32_t chain_paddr; // offset to next filesystem signature (0)
  416.    uint32_t spare[10]; // zerofill
  417.    uint32_t mountflags; // default _MOUNT_* from sys/iomsg.h (0)
  418.    char mountpoint[4]; // default mountpoint for image ("/" + "\0\0\0")
  419. } image_header_t;
  420.  
  421.  
  422. typedef struct __attribute__((packed)) image_trailer_v1_s
  423. {
  424.    uint32_t cksum; // checksum from start of header to start of trailer
  425. } image_trailer_v1_t; // NOTE: this is the same structure as startup_trailer_v1_t
  426.  
  427.  
  428. // NOTE: the checksums in this trailer will only be valid until the first non-startup bootstrap binary (e.g., startup-verifier, procnto, ...) is invoked.
  429. // Because bootstrap binaries execute in-place, their data segments will change once the programs are running.
  430. // Hence, any checksum validation would need to be done either by the boot loader / IFS or by the startup.
  431. typedef struct __attribute__((packed)) image_trailer_v2_s
  432. {
  433.    uint8_t sha512[64]; // SHA512 from start of image header to start of trailer
  434.    uint32_t cksum; // checksum from start of header to start of this member
  435. } image_trailer_v2_t; // NOTE: this is the same structure as startup_trailer_v2_t
  436.  
  437.  
  438. // Executable and Linkable Format master header structure type definition
  439. typedef struct __attribute__((packed)) elf_header_s
  440. {
  441.    union __attribute__((packed))
  442.    {
  443.       struct __attribute__((packed))
  444.       {
  445.          uint8_t magic[4];                     // offset 0: "\x7f" + "ELF"
  446.          uint8_t platform_size;                // offset 4: 1 = 32-bit, 2 = 64-bit
  447.          uint8_t endianness;                   // offset 5: 1 = little endian, 2 = big endian
  448.          uint8_t header_version;               // offset 6: typically 1
  449.          uint8_t os_abi;                       // offset 7: 0 = SysV, 1 = HP/UX, 2 = NetBSD, 3 = Linux, 4 = GNU/Hurd, 6 = Solaris, 7 = AIX, 8 = IRIX, 9 = FreeBSD, 10 = Tru64, 11 = Novell, 12 = OpenBSD, 13 = OpenVMS, 14 = NonStop kernel, 15 = AROS, 16 = FenixOS, 17 = Nuxi CloudABI, 18 = OpenVOS
  450.          uint8_t spare[8];                     // offset 8: zeroes
  451.          uint16_t type;                        // offset 16: 1 = relocatable, 2 = executable, 3 = shared, 4 = core dump
  452.          uint16_t instruction_set;             // offset 18: 2 = Sparc, 3 = i386, 8 = MIPS, 20 = PowerPC, 40 = ARM, 42 = SuperH, 50 = IA-64, 62 = x86_64, 183 = AArch64, 243 = RISC-V
  453.          uint32_t elf_version;                 // offset 20: typically 1
  454.       } elf;
  455.       struct __attribute__((packed)) // size == 52
  456.       {
  457.          uint8_t magic[4];                     // offset 0: "\x7f" + "ELF"
  458.          uint8_t platform_size;                // offset 4: 1 = 32-bit, 2 = 64-bit
  459.          uint8_t endianness;                   // offset 5: 1 = little endian, 2 = big endian
  460.          uint8_t header_version;               // offset 6: typically 1
  461.          uint8_t os_abi;                       // offset 7: 0 = SysV, 1 = HP/UX, 2 = NetBSD, 3 = Linux, 4 = GNU/Hurd, 6 = Solaris, 7 = AIX, 8 = IRIX, 9 = FreeBSD, 10 = Tru64, 11 = Novell, 12 = OpenBSD, 13 = OpenVMS, 14 = NonStop kernel, 15 = AROS, 16 = FenixOS, 17 = Nuxi CloudABI, 18 = OpenVOS
  462.          uint8_t spare[8];                     // offset 8: zeroes
  463.          uint16_t type;                        // offset 16: 1 = relocatable, 2 = executable, 3 = shared, 4 = core dump
  464.          uint16_t instruction_set;             // offset 18: 2 = Sparc, 3 = i386, 8 = MIPS, 20 = PowerPC, 40 = ARM, 42 = SuperH, 50 = IA-64, 62 = x86_64, 183 = AArch64, 243 = RISC-V
  465.          uint32_t elf_version;                 // offset 20: typically 1
  466.          uint32_t entrypoint_offset;           // offset 24: offset to program entrypoint
  467.          uint32_t program_header_table_offset; // offset 28: offset to program header table
  468.          uint32_t section_header_table_offset; // offset 32: offset to section header table
  469.          uint32_t flags;                       // offset 36: flags (architecture-dependent, none for x86)
  470.          uint16_t header_size;                 // offset 40: size of ELF header, 52 for 32-bit ELF and 64 for 64-bit ELF -- DO NOT USE sizeof() ON THE elf_header_s STRUCT BECAUSE OF THE UNION! WRITE THE CORRECT SIZE YOURSELF!
  471.          uint16_t program_header_item_size;    // offset 42: size of an entry in the program header table
  472.          uint16_t program_header_table_len;    // offset 44: number of entries in the program header table
  473.          uint16_t section_header_item_size;    // offset 46: size of an entry in the section header table
  474.          uint16_t section_header_table_len;    // offset 48: number of entries in the section header table
  475.          uint16_t section_header_names_idx;    // offset 50: index of the entry in the section header table that contains the section names
  476.       } elf32; // size == 52
  477.       struct __attribute__((packed)) // size == 64
  478.       {
  479.          uint8_t magic[4];                     // offset 0: "\x7f" + "ELF"
  480.          uint8_t platform_size;                // offset 4: 1 = 32-bit, 2 = 64-bit
  481.          uint8_t endianness;                   // offset 5: 1 = little endian, 2 = big endian
  482.          uint8_t header_version;               // offset 6: typically 1
  483.          uint8_t os_abi;                       // offset 7: 0 = SysV, 1 = HP/UX, 2 = NetBSD, 3 = Linux, 4 = GNU/Hurd, 6 = Solaris, 7 = AIX, 8 = IRIX, 9 = FreeBSD, 10 = Tru64, 11 = Novell, 12 = OpenBSD, 13 = OpenVMS, 14 = NonStop kernel, 15 = AROS, 16 = FenixOS, 17 = Nuxi CloudABI, 18 = OpenVOS
  484.          uint8_t spare[8];                     // offset 8: zeroes
  485.          uint16_t type;                        // offset 16: 1 = relocatable, 2 = executable, 3 = shared, 4 = core dump
  486.          uint16_t instruction_set;             // offset 18: 2 = Sparc, 3 = i386, 8 = MIPS, 20 = PowerPC, 40 = ARM, 42 = SuperH, 50 = IA-64, 62 = x86_64, 183 = AArch64, 243 = RISC-V
  487.          uint32_t elf_version;                 // offset 20: typically 1
  488.          uint64_t entrypoint_offset;           // offset 24: program entry offset
  489.          uint64_t program_header_table_offset; // offset 32: offset to program header table
  490.          uint64_t section_header_table_offset; // offset 40: offset to section header table
  491.          uint32_t flags;                       // offset 48: flags (architecture-dependent, none for x86)
  492.          uint16_t header_size;                 // offset 52: size of ELF header, 52 for 32-bit ELF and 64 for 64-bit ELF
  493.          uint16_t program_header_item_size;    // offset 54: size of an entry in the program header table
  494.          uint16_t program_header_table_len;    // offset 56: number of entries in the program header table
  495.          uint16_t section_header_item_size;    // offset 58: size of an entry in the section header table
  496.          uint16_t section_header_table_len;    // offset 60: number of entries in the section header table
  497.          uint16_t section_header_names_idx;    // offset 62: index of the entry in the section header table that contains the section names
  498.       } elf64; // size == 64
  499.    } u;
  500. } elf_header_t;
  501.  
  502.  
  503. // Executable and Linkable Format program header structure type definition
  504. typedef struct __attribute__((packed)) elf_program_header_s
  505. {
  506.    union __attribute__((packed))
  507.    {
  508.       struct __attribute__((packed))
  509.       {
  510.          uint32_t segment_type; // offset 0: type of segment (0: unused table entry, 1: loadable, 2: dynamic linking information, 3: interpreter information, 4: auxiliary information, 5: reserved, 6: this very segment, 7: TLS template)
  511.       } elf;
  512.       struct __attribute__((packed)) // size == 32
  513.       {
  514.          uint32_t segment_type;   // offset 0: type of segment (0: unused table entry, 1: loadable, 2: dynamic linking information, 3: interpreter information, 4: auxiliary information, 5: reserved, 6: this very segment, 7: TLS template)
  515.          uint32_t file_offset;    // offset 4: file offset of this segment
  516.          uint32_t virtual_addr;   // offset 8: virtual address where this segment should be mapped in memory
  517.          uint32_t physical_addr;  // offset 12: on systems where this is relevant, PHYSICAL address where this segment should be mapped in memory
  518.          uint32_t size_in_file;   // offset 16: size of this segment in the ELF file (may be zero)
  519.          uint32_t size_in_memory; // offset 20: size of this segment in memory (may be zero)
  520.          uint32_t segment_flags;  // offset 24: bitmap of segment flags (1: executable, 2: writable, 4: readable)
  521.          uint32_t alignment;      // offset 28: memory alignment (0 or 1 mean non alignment, else must be a power of 2 where virtual_addr == file_offset % alignment)
  522.       } elf32; // size == 32
  523.       struct __attribute__((packed)) // size == 56
  524.       {
  525.          uint32_t segment_type;   // offset 0: type of segment (0: unused table entry, 1: loadable, 2: dynamic linking information, 3: interpreter information, 4: auxiliary information, 5: reserved, 6: this very segment, 7: TLS template)
  526.          uint32_t segment_flags;  // offset 4: bitmap of segment flags (1: executable, 2: writable, 4: readable)
  527.          uint64_t file_offset;    // offset 8: file offset of this segment
  528.          uint64_t virtual_addr;   // offset 16: virtual address where this segment should be mapped in memory
  529.          uint64_t physical_addr;  // offset 24: on systems where this is relevant, PHYSICAL address where this segment should be mapped in memory
  530.          uint64_t size_in_file;   // offset 32: size of this segment in the ELF file (may be zero)
  531.          uint64_t size_in_memory; // offset 40: size of this segment in memory (may be zero)
  532.          uint64_t alignment;      // offset 48: memory alignment (0 or 1 mean non alignment, else must be a power of 2 where virtual_addr == file_offset % alignment)
  533.       } elf64; // size == 56
  534.    } u;
  535. } elf_program_header_t;
  536.  
  537.  
  538. // Executable and Linkable Format section header structure type definition
  539. typedef struct __attribute__((packed)) elf_section_header_s
  540. {
  541.    union __attribute__((packed))
  542.    {
  543.       struct __attribute__((packed)) // size == 40
  544.       {
  545.          uint32_t name_offset;  // offset 0: offset in the string table of the name of this section
  546.          uint32_t type;         // offset 4:
  547.          uint32_t flags;        // offset 8:
  548.          uint32_t virtual_addr; // offset 12: address in virtual memory where this section may be loaded
  549.          uint32_t file_offset;  // offset 16: offset of this section in the ELF file
  550.          uint32_t size;         // offset 20: size of this section
  551.          uint32_t linked_index; // offset 24: optional section index of an associated section
  552.          uint32_t info;         // offset 28: optional extra information
  553.          uint32_t alignment;    // offset 32: required memory alignment (must be a power of 2)
  554.          uint32_t entry_size;   // offset 36: for table-like sections, size of an element in the table
  555.       } elf32; // size == 40
  556.       struct __attribute__((packed)) // size == 64
  557.       {
  558.          uint32_t name_offset;  // offset 0: offset in the string table of the name of this section
  559.          uint32_t type;         // offset 4:
  560.          uint64_t flags;        // offset 8:
  561.          uint64_t virtual_addr; // offset 16: address in virtual memory where this section may be loaded
  562.          uint64_t file_offset;  // offset 24: offset of this section in the ELF file
  563.          uint64_t size;         // offset 32: size of this section
  564.          uint32_t linked_index; // offset 40: optional section index of an associated section
  565.          uint32_t info;         // offset 44: optional extra information
  566.          uint64_t alignment;    // offset 48: required memory alignment (must be a power of 2)
  567.          uint64_t entry_size;   // offset 56: for table-like sections, size of an element in the table
  568.       } elf64; // size == 64
  569.    } u;
  570. } elf_section_header_t;
  571.  
  572.  
  573. // Executable and Linkable Format dynamic section entry structure type definition
  574. typedef struct __attribute__((packed)) elf_dynamic_section_entry_s
  575. {
  576.    union __attribute__((packed))
  577.    {
  578.       struct __attribute__((packed)) // size == 8
  579.       {
  580.          int32_t tag; // dynamic entry type (one of ELF_DT_xxx #defines)
  581.          union
  582.          {
  583.             uint32_t as_integer; // value as integer
  584.             uint32_t as_pointer; // value as address
  585.          } value;
  586.       } elf32; // size == 8
  587.       struct __attribute__((packed)) // size == 16
  588.       {
  589.          int64_t tag; // dynamic entry type (one of ELF_DT_xxx #defines)
  590.          union
  591.          {
  592.             uint64_t as_integer; // value as intege
  593.             uint64_t as_pointer; // value as address
  594.          } value;
  595.       } elf64; // size == 16
  596.    } u;
  597. } elf_dynamic_section_entry_t;
  598.  
  599.  
  600. #ifdef _MSC_VER
  601. #pragma pack(pop)
  602. #endif // _MSC_VER
  603.  
  604.  
  605. typedef struct parms_s
  606. {
  607.    int dperms; // directory permissions (e.g. 0755)
  608.    int perms; // file permissions (e.g. 0644)
  609.    int uid; // owner user ID (e.g. 0 = root)
  610.    int gid; // owner group ID (e.g. 0 = root)
  611.    int st_mode; // entry type (e.g. S_IFREG for files) and permissions
  612.    uint32_t mtime; // entry's modification time POSIX timestamp - set to UINT32_MAX to use the concerned files' mtime on the build host
  613.    uint32_t mtime_for_inline_files; // same as above but only for files that don't exist on the build host (i.e. files with an explicit content blob)
  614.    char prefix[MAXPATHLEN]; // install path (e.g. "proc/boot")
  615.    bool should_follow_symlinks; // follow symlinks
  616.    bool should_autosymlink_dylib; // dynamic libraries should be written under their official SONAME and a named symlink be created pointing at them
  617.    bool is_compiled_bootscript; // entry has [+script] attribute
  618.    int extra_ino_flags; // bitmap of extra inode flags (IFS_INO_xxx)
  619.    char search[10 * MAXPATHLEN]; // binary search path (the default one will be constructed at startup)
  620.  
  621.    uint8_t *data;
  622.    size_t datalen;
  623. } parms_t;
  624.  
  625.  
  626. // global variables
  627. static char line_buffer[4096]; // scrap buffer for the IFS build file parser
  628. static uint32_t image_base = 4 * 1024 * 1024; // default image base, as per QNX docs -- can be changed with the [image=XXXX] attribute in the IFS build file
  629. static uint32_t image_end = UINT32_MAX; // default image end (no limit)
  630. static uint32_t image_maxsize = UINT32_MAX; // default image max size (no limit)
  631. static uint32_t image_totalsize = 0; // image total size, measured once all the blocks have been written to the output IFS file
  632. static uint32_t image_align = 4; // default image alignment, as per QNX docs
  633. static uint32_t image_kernel_ino = 0;
  634. static uint32_t image_bootscript_ino = 0;
  635. #if defined(__x86_64__)
  636. static char image_processor[16] = "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)
  637. #elif defined(__aarch64__)
  638. static char image_processor[16] = "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)
  639. #else // unknown platform
  640. #error Please port ifstool to this platform
  641. #endif
  642. static char *buildfile_pathname = NULL; // pathname of IFS build file
  643. static char *current_line = NULL; // copy of current line in IFS build file
  644. static int lineno = 0; // current line number in IFS build file
  645. static char *QNX_TARGET = NULL; // value of the $QNX_TARGET environment variable
  646. static char *MKIFS_PATH = NULL; // value of the $MKIFS_PATH environment variable (may contain references to $QNX_TARGET). Initialized by this program if empty.
  647.  
  648.  
  649. // prototypes of local functions
  650. static void sha512_private_transform (SHA512_CTX *context, const uint64_t *data); // used internally in SHA512_Update() and SHA512_Final()
  651. static void SHA512_Init (SHA512_CTX *context);
  652. static void SHA512_Update (SHA512_CTX *context, void *data, size_t len);
  653. static void SHA512_Final (uint8_t digest[SHA512_DIGEST_LENGTH], SHA512_CTX *context);
  654. static uint8_t *SHA512 (void *data, size_t data_len, uint8_t *digest); // computes a SHA-512 in one pass (shortcut for SHA512_Init(), SHA512_Update() N times and SHA512_Final())
  655. static 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
  656. static long long read_integer (const char *str); // reads an integer number for a string that may be specified in either hex, octal or decimal base, and may have an optional unit suffix (k, m, g, t)
  657. static void hex_fprintf (FILE *fp, const uint8_t *data, size_t data_size, int howmany_columns, const char *fmt, ...); // hexdump-style formatted output to a file stream (which may be stdout/stderr)
  658. static char *binary (const uint8_t x, char char_for_zero, char char_for_one); // returns the binary representation of byte 'x' as a string
  659. static char *describe_uint8 (const uint8_t x, const char *bitwise_stringdescs[8]); // returns the ORed description of byte 'x' according to the description strings for each bit
  660. static char *read_filecontents (const char *pathname, const char *search_path, uint8_t **databuf, size_t *datalen); // locates pathname among MKIFS_PATH, reads it, places its contents in a buffer (caller frees) and returns a pointer to the resolved pathname (static string)
  661. static int fwrite_filecontents (const char *pathname, FILE *fp); // dumps the contents of pathname into fp
  662. static int relative_offset_of_in (const char *name, const char *string_table, const size_t table_len); // returns the relative offset of a particular string in a string table
  663. static elf_section_header_t *elf_get_section_header_by_name (const elf_header_t *elf, const char *section_name); // get a pointer to a named section header in an ELF file
  664. static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp); // writes the given filesystem entry into fp (without its contents)
  665. static size_t add_fsentry (fsentry_t **fsentries, size_t *fsentry_count, parms_t *entry_parms, const char *stored_pathname, const char *buildhost_pathname); // stack up a new filesystem entry
  666. static int fsentry_compare_pathnames_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by pathnames
  667. static void update_MKIFS_PATH (const char *processor);
  668. static int dump_ifs_info (const char *ifs_pathname, bool want_everything); // dumps detailed info about a particular IFS file on the standard output, returns 0 on success and >0 on error
  669.  
  670.  
  671. static void sha512_private_transform (SHA512_CTX *context, const uint64_t *data)
  672. {
  673.    // logical functions used in SHA-384 and SHA-512
  674.    #define S64(b,x)      (((x) >> (b)) | ((x) << (64 - (b)))) // 64-bit rotate right
  675.    #define Ch(x,y,z)     (((x) & (y)) ^ ((~(x)) & (z)))
  676.    #define Maj(x,y,z)    (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
  677.    #define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
  678.    #define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
  679.    #define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ ((x) >> 7))
  680.    #define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ ((x) >> 6))
  681.  
  682.    // hash constant words K for SHA-384 and SHA-512
  683.    static const uint64_t K512[80] = {
  684.       0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
  685.       0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
  686.       0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
  687.       0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
  688.       0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
  689.       0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
  690.       0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
  691.       0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
  692.       0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
  693.       0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
  694.    };
  695.  
  696.    uint64_t     a, b, c, d, e, f, g, h, s0, s1;
  697.    uint64_t     T1, T2, *W512 = (uint64_t *) context->buffer;
  698.    int j;
  699.  
  700.    // initialize registers with the prev. intermediate value
  701.    a = context->state[0]; b = context->state[1]; c = context->state[2]; d = context->state[3]; e = context->state[4]; f = context->state[5]; g = context->state[6]; h = context->state[7];
  702.  
  703.    for (j = 0; j < 16; j++)
  704.    {
  705. #if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
  706.       W512[j] = __builtin_bswap64 (*data); // convert to host byte order
  707. #elif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
  708.       W512[j] = *data;
  709. #else // __BYTE_ORDER__ == ???
  710. #error Please port this SHA-512 code to your exotic endianness platform. What are you compiling this on? PDP? Honeywell?
  711. #endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
  712.  
  713.       // apply the SHA-512 compression function to update a..h
  714.       T1 = h + Sigma1_512 (e) + Ch (e, f, g) + K512[j] + W512[j];
  715.       T2 = Sigma0_512 (a) + Maj (a, b, c);
  716.  
  717.       // update registers
  718.       h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2;
  719.  
  720.       data++;
  721.    }
  722.  
  723.    for (; j < 80; j++)
  724.    {
  725.       // part of the message block expansion
  726.       s0 = W512[(j + 1) & 0x0f];
  727.       s0 = sigma0_512 (s0);
  728.       s1 = W512[(j + 14) & 0x0f];
  729.       s1 = sigma1_512 (s1);
  730.  
  731.       // apply the SHA-512 compression function to update a..h
  732.       T1 = h + Sigma1_512 (e) + Ch (e, f, g) + K512[j] + (W512[j & 0x0f] += s1 + W512[(j + 9) & 0x0f] + s0);
  733.       T2 = Sigma0_512 (a) + Maj (a, b, c);
  734.  
  735.       // update registers
  736.       h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2;
  737.    }
  738.  
  739.    // compute the current intermediate hash value
  740.    context->state[0] += a; context->state[1] += b; context->state[2] += c; context->state[3] += d; context->state[4] += e; context->state[5] += f; context->state[6] += g; context->state[7] += h;
  741.  
  742.    // clean up
  743.    a = b = c = d = e = f = g = h = T1 = T2 = 0;
  744.    #undef sigma1_512
  745.    #undef sigma0_512
  746.    #undef Sigma1_512
  747.    #undef Sigma0_512
  748.    #undef Maj
  749.    #undef Ch
  750.    #undef S64
  751.    return;
  752. }
  753.  
  754.  
  755. static void SHA512_Init (SHA512_CTX *context)
  756. {
  757.    // initial hash value H for SHA-512
  758.    static const uint64_t sha512_initial_hash_value[8] = {
  759.       0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
  760.    };
  761.  
  762.    memcpy (context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
  763.    memset (context->buffer, 0, SHA512_BLOCK_LENGTH);
  764.    context->bitcount[0] = context->bitcount[1] = 0;
  765. }
  766.  
  767.  
  768. void SHA512_Update (SHA512_CTX *context, void *datain, size_t len)
  769. {
  770.    #define ADDINC128(w,n) do { \
  771.            (w)[0] += (uint64_t) (n); \
  772.            if ((w)[0] < (n)) \
  773.                    (w)[1]++; \
  774.    } while (0) // macro for incrementally adding the unsigned 64-bit integer n to the unsigned 128-bit integer (represented using a two-element array of 64-bit words
  775.  
  776.    size_t freespace, usedspace;
  777.    const uint8_t *data = (const uint8_t *) datain;
  778.  
  779.    if (len == 0)
  780.       return; // calling with empty data is valid - we do nothing
  781.  
  782.    usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
  783.    if (usedspace > 0)
  784.    {
  785.       // calculate how much free space is available in the buffer
  786.       freespace = SHA512_BLOCK_LENGTH - usedspace;
  787.  
  788.       if (len >= freespace)
  789.       {
  790.          // fill the buffer completely and process it
  791.          memcpy (&context->buffer[usedspace], data, freespace);
  792.          ADDINC128 (context->bitcount, freespace << 3);
  793.          len -= freespace;
  794.          data += freespace;
  795.          sha512_private_transform (context, (uint64_t *) context->buffer);
  796.       }
  797.       else
  798.       {
  799.          // the buffer is not full yet
  800.          memcpy (&context->buffer[usedspace], data, len);
  801.          ADDINC128 (context->bitcount, len << 3);
  802.  
  803.          // clean up
  804.          usedspace = freespace = 0;
  805.          return;
  806.       }
  807.    }
  808.  
  809.    while (len >= SHA512_BLOCK_LENGTH)
  810.    {
  811.       // process as many complete blocks as we can
  812.       sha512_private_transform (context, (uint64_t *) data);
  813.       ADDINC128 (context->bitcount, SHA512_BLOCK_LENGTH << 3);
  814.       len -= SHA512_BLOCK_LENGTH;
  815.       data += SHA512_BLOCK_LENGTH;
  816.    }
  817.  
  818.    if (len > 0)
  819.    {
  820.       // save leftovers
  821.       memcpy (context->buffer, data, len);
  822.       ADDINC128 (context->bitcount, len << 3);
  823.    }
  824.  
  825.    // clean up
  826.    usedspace = freespace = 0;
  827.    #undef ADDINC128
  828.    return;
  829. }
  830.  
  831.  
  832. static void SHA512_Final (uint8_t digest[SHA512_DIGEST_LENGTH], SHA512_CTX *context)
  833. {
  834.    #define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
  835.  
  836.    size_t usedspace;
  837.    union { uint8_t *as_bytes; uint64_t *as_uint64s; } cast_var = { NULL };
  838.  
  839.    // if no digest buffer is passed, don't bother finalizing the computation
  840.    if (digest != NULL)
  841.    {
  842.       usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
  843.  
  844. #if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
  845.       context->bitcount[0] = __builtin_bswap64 (context->bitcount[0]); // convert from host byte order
  846.       context->bitcount[1] = __builtin_bswap64 (context->bitcount[1]); // convert from host byte order
  847. #endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
  848.  
  849.       if (usedspace > 0)
  850.       {
  851.          // begin padding with a 1 bit
  852.          context->buffer[usedspace++] = 0x80;
  853.  
  854.          if (usedspace <= SHA512_SHORT_BLOCK_LENGTH)
  855.             memset (&context->buffer[usedspace], 0, SHA512_SHORT_BLOCK_LENGTH - usedspace); // set-up for the last transform
  856.          else
  857.          {
  858.             if (usedspace < SHA512_BLOCK_LENGTH)
  859.                memset (&context->buffer[usedspace], 0, SHA512_BLOCK_LENGTH - usedspace);
  860.  
  861.             sha512_private_transform (context, (uint64_t *) context->buffer); // do second-to-last transform
  862.             memset (context->buffer, 0, SHA512_BLOCK_LENGTH - 2); // and set-up for the last transform
  863.          }
  864.       }
  865.       else // usedspace == 0
  866.       {
  867.          memset (context->buffer, 0, SHA512_SHORT_BLOCK_LENGTH); // prepare for final transform
  868.          *context->buffer = 0x80; // begin padding with a 1 bit
  869.       }
  870.  
  871.       // store the length of input data (in bits)
  872.       cast_var.as_bytes = context->buffer;
  873.       cast_var.as_uint64s[SHA512_SHORT_BLOCK_LENGTH / 8 + 0] = context->bitcount[1];
  874.       cast_var.as_uint64s[SHA512_SHORT_BLOCK_LENGTH / 8 + 1] = context->bitcount[0];
  875.  
  876.       // final transform
  877.       sha512_private_transform (context, (uint64_t *) context->buffer);
  878.  
  879.       // save the hash data for output
  880. #if __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
  881.       for (int j = 0; j < 8; j++)
  882.          context->state[j] = __builtin_bswap64 (context->state[j]); // convert to host byte order
  883. #endif // __BYTE_ORDER__ ==  __ORDER_LITTLE_ENDIAN__
  884.       memcpy (digest, context->state, SHA512_DIGEST_LENGTH);
  885.    }
  886.  
  887.    // zero out state data
  888.    memset (context, 0, sizeof (SHA512_CTX));
  889.    #undef SHA512_SHORT_BLOCK_LENGTH
  890.    return;
  891. }
  892.  
  893.  
  894. static uint8_t *SHA512 (void *data, size_t data_len, uint8_t *digest_or_NULL)
  895. {
  896.    // computes the SHA-512 hash of a block of data in one pass and write it to digest, or to a static buffer if NULL
  897.    // returns the STRING REPRESENTATION of digest in a statically-allocated string
  898.  
  899.    static thread_local uint8_t static_digest[SHA512_DIGEST_LENGTH] = "";
  900.    static thread_local char digest_as_string[2 * SHA512_DIGEST_LENGTH + 1] = "";
  901.  
  902.    SHA512_CTX ctx;
  903.    size_t byte_index;
  904.  
  905.    SHA512_Init (&ctx);
  906.    SHA512_Update (&ctx, data, data_len);
  907.    if (digest_or_NULL == NULL)
  908.       digest_or_NULL = static_digest;
  909.    SHA512_Final (digest_or_NULL, &ctx);
  910.  
  911.    for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
  912.       sprintf (&digest_as_string[2 * byte_index], "%02x", digest_or_NULL[byte_index]);
  913.    return (digest_as_string);
  914. }
  915.  
  916.  
  917. static int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness)
  918. {
  919.    // computes the checksum of an IFS image or startup section, i.e. from the start of the header to the end of the trailer minus the last 4 bytes where the checksum is stored
  920.  
  921.    uint8_t accumulator[4] = { 0, 0, 0, 0 };
  922.    const char *current_char_ptr;
  923.    int32_t image_cksum;
  924.    size_t i;
  925.  
  926.    image_cksum = 0;
  927.    current_char_ptr = data;
  928.    for (i = 0; i < data_len; i++)
  929.    {
  930.       accumulator[i % 4] = *current_char_ptr;
  931.       if (i % 4 == 3)
  932.          if (is_foreign_endianness)
  933.             image_cksum += (accumulator[3] << 0) + (accumulator[2] << 8) + (accumulator[1] << 16) + (accumulator[0] << 24);
  934.          else
  935.             image_cksum += (accumulator[0] << 0) + (accumulator[1] << 8) + (accumulator[2] << 16) + (accumulator[3] << 24);
  936.       current_char_ptr++;
  937.    }
  938.  
  939.    return (is_foreign_endianness ? __builtin_bswap32 (-image_cksum) : -image_cksum);
  940. }
  941.  
  942.  
  943. static long long read_integer (const char *str)
  944. {
  945.    // reads a number for a string that may be specified in either hex, octal or decimal base, and may have an optional unit suffix (k, m, g, t)
  946.  
  947.    char *endptr = NULL;
  948.    long long ret = strtoll (str, &endptr, 0); // use strtoll() to handle hexadecimal (0x...), octal (0...) and decimal (...) bases
  949.    if (endptr != NULL)
  950.    {
  951.       if ((*endptr == 'k') || (*endptr == 'K')) ret *= (size_t) 1024;
  952.       else if ((*endptr == 'm') || (*endptr == 'M')) ret *= (size_t) 1024 * 1024;
  953.       else if ((*endptr == 'g') || (*endptr == 'G')) ret *= (size_t) 1024 * 1024 * 1024;
  954.       else if ((*endptr == 't') || (*endptr == 'T')) ret *= (size_t) 1024 * 1024 * 1024 * 1024; // future-proof enough, I suppose?
  955.    }
  956.    return (ret);
  957. }
  958.  
  959.  
  960. static void hex_fprintf (FILE *fp, const uint8_t *data, size_t data_size, int howmany_columns, const char *fmt, ...)
  961. {
  962.    // this function logs hexadecimal data to an opened file pointer (or to stdout/stderr)
  963.  
  964.    va_list argptr;
  965.    size_t index;
  966.    int i;
  967.  
  968.    // concatenate all the arguments in one string and write it to the file
  969.    va_start (argptr, fmt);
  970.    vfprintf (fp, fmt, argptr);
  971.    va_end (argptr);
  972.  
  973.    // for each row of howmany_columns bytes of data...
  974.    for (index = 0; index < data_size; index += howmany_columns)
  975.    {
  976.       fprintf (fp, "    %05zu  ", index); // print array address of row
  977.       for (i = 0; i < howmany_columns; i++)
  978.          if (index + i < data_size)
  979.             fprintf (fp, " %02X", data[index + i]); // if row contains data, print data as hex bytes
  980.          else
  981.             fprintf (fp, "   "); // else fill the space with blanks
  982.       fprintf (fp, "   ");
  983.       for (i = 0; i < howmany_columns; i++)
  984.          if (index + i < data_size)
  985.             fputc ((data[index + i] >= 32) && (data[index + i] < 127) ? data[index + i] : '.', fp); // now if row contains data, print data as ASCII
  986.          else
  987.             fputc (' ', fp); // else fill the space with blanks
  988.       fputc ('\n', fp);
  989.    }
  990.  
  991.    return; // and return
  992. }
  993.  
  994.  
  995. static char *binary (const uint8_t x, char char_for_zero, char char_for_one)
  996. {
  997.    // returns the binary representation of x as a string
  998.  
  999.    static thread_local char outstr[9] = "00000000";
  1000.    for (int i = 0; i < 8; i++)
  1001.       outstr[i] = (x & (0x80 >> i) ? char_for_one : char_for_zero);
  1002.    return (outstr);
  1003. }
  1004.  
  1005.  
  1006. static char *describe_uint8 (const uint8_t x, const char *bitwise_stringdescs[8])
  1007. {
  1008.    // returns the ORed description of byte 'x' according to the description strings for each bit
  1009.  
  1010.    static thread_local char *default_bitstrings[8] = { "bit0", "bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7" };
  1011.    static thread_local char outstr[8 * 64] = "";
  1012.  
  1013.    outstr[0] = 0;
  1014.    for (int i = 0; i < 8; i++)
  1015.       if (x & (1 << i))
  1016.       {
  1017.          if (outstr[0] != 0)
  1018.             strcat (outstr, "|");
  1019.          strcat (outstr, ((bitwise_stringdescs != NULL) && (*bitwise_stringdescs[i] != 0) ? bitwise_stringdescs[i] : default_bitstrings[i]));
  1020.       }
  1021.    return (outstr);
  1022. }
  1023.  
  1024.  
  1025. static char *read_filecontents (const char *pathname, const char *search_path, uint8_t **databuf, size_t *datalen)
  1026. {
  1027.    // locates pathname among MKIFS_PATH, and places its contents in a buffer (caller frees). Returns resolved pathname (static buffer) or NULL.
  1028.  
  1029.    static thread_local char final_pathname[MAXPATHLEN];
  1030.  
  1031.    const char *nextsep;
  1032.    const char *token;
  1033.    FILE *fp;
  1034.  
  1035.    // is it an absolute pathname (POSIX and Windows variants) ?
  1036.    if (IS_DIRSEP (pathname[0]) || (isalpha (pathname[0]) && (pathname[1] == ':') && IS_DIRSEP (pathname[2])))
  1037.       strcpy (final_pathname, pathname); // in this case, it MUST exist at its designated location (either absolute or relative to the current working directory)
  1038.    else // the path is relative, search it among the search paths we have
  1039.    {
  1040.       // construct a potential final path using each element of the search path
  1041.       token = (*search_path != 0 ? search_path : NULL);
  1042.       nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP_STR)] : NULL);
  1043.       while (token != NULL)
  1044.       {
  1045.          sprintf (final_pathname, "%.*s/%s", (int) (nextsep - token), token, pathname);
  1046.          if (access (final_pathname, 0) == 0)
  1047.             break; // if a file can indeed be found at this location, stop searching
  1048.  
  1049.          token = (*nextsep != 0 ? nextsep + 1 : NULL);
  1050.          nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP_STR)] : NULL);
  1051.       }
  1052.  
  1053.       // have we exhausted all possibilities ?
  1054.       if (token == NULL)
  1055.       {
  1056.          errno = ENOENT;
  1057.          return (NULL); // file not found, return with ENOENT
  1058.       }
  1059.    }
  1060.  
  1061.    // now open and read the file
  1062.    fp = fopen (final_pathname, "rb");
  1063.    if (fp == NULL)
  1064.       return (NULL); // unexistent file (errno is set to ENOENT)
  1065.  
  1066.    fseek (fp, 0, SEEK_END);
  1067.    *datalen = ftell (fp); // measure file length
  1068.    fseek (fp, 0, SEEK_SET);
  1069.    *databuf = malloc (*datalen);
  1070.    if (*databuf == NULL)
  1071.    {
  1072.       fclose (fp);
  1073.       *datalen = 0;
  1074.       return (NULL); // out of memory (errno is set to ENOMEM)
  1075.    }
  1076.    if (fread (*databuf, 1, *datalen, fp) != *datalen) // read the file in whole
  1077.    {
  1078.       fclose (fp);
  1079.       *datalen = 0;
  1080.       return (NULL); // short read (errno is set)
  1081.    }
  1082.    fclose (fp); // close the file
  1083.  
  1084.    return (final_pathname); // file was read successfully and its content put in databuf with size datalen
  1085. }
  1086.  
  1087.  
  1088. static int fwrite_filecontents (const char *pathname, FILE *fp)
  1089. {
  1090.    // dumps the binary contents of pathname to fp
  1091.  
  1092.    uint8_t *blob_buffer;
  1093.    size_t blob_size;
  1094.    FILE *blob_fp;
  1095.    int ret;
  1096.  
  1097.    blob_fp = fopen (pathname, "rb");
  1098.    if (blob_fp == NULL)
  1099.       return (-1); // errno is set
  1100.  
  1101.    fseek (blob_fp, 0, SEEK_END);
  1102.    blob_size = ftell (blob_fp);
  1103.    blob_buffer = malloc (blob_size);
  1104.    if (blob_buffer == NULL)
  1105.    {
  1106.       fclose (blob_fp);
  1107.       return (-1); // errno is set to ENOMEM
  1108.    }
  1109.    fseek (blob_fp, 0, SEEK_SET);
  1110.    fread (blob_buffer, 1, blob_size, blob_fp);
  1111.    fclose (blob_fp);
  1112.  
  1113.    ret = (int) fwrite (blob_buffer, 1, blob_size, fp);
  1114.    fflush (fp); // force flush to disk, because the C stream API is *buffered*
  1115.    free (blob_buffer);
  1116.    return (ret);
  1117. }
  1118.  
  1119.  
  1120. static int relative_offset_of_in (const char *name, const char *string_table, const size_t table_len)
  1121. {
  1122.    int name_len = (int) strlen (name) + 1;
  1123.    WELLMANNERED_ASSERT (name_len < table_len, "bad call (name longer than string table)");
  1124.    for (int idx = 0; idx <= table_len - name_len; idx++)
  1125.       if (memcmp (&string_table[idx], name, name_len) == 0)
  1126.          return (idx);
  1127.    WELLMANNERED_ASSERT (false, "bad call (name '%s' not found in string table)", name);
  1128.    return (0);
  1129. }
  1130.  
  1131.  
  1132. static elf_section_header_t *elf_get_section_header_by_name (const elf_header_t *elf, const char *section_name)
  1133. {
  1134.    elf_section_header_t *shdr_shstrtab; // section header of the section header strings table
  1135.    elf_section_header_t *shdr;
  1136.    size_t table_count;
  1137.    size_t table_index;
  1138.    char *shstrtab; // section header strings table
  1139.    char *name;
  1140.  
  1141.    shdr_shstrtab = (elf_section_header_t *) ((uint8_t *) elf + ELF_GET_NUMERIC (elf, elf, section_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, section_header_item_size) * ELF_GET_NUMERIC (elf, elf, section_header_names_idx)); // quick access to section header for the section that contains the section names
  1142.    shstrtab = ((uint8_t *) elf + ELF_GET_NUMERIC (elf, shdr_shstrtab, file_offset)); // locate the start of the strings table that contains the section names
  1143.  
  1144.    // cycle through the sections table
  1145.    table_count = ELF_GET_NUMERIC (elf, elf, section_header_table_len);
  1146.    for (table_index = 0; table_index < table_count; table_index++)
  1147.    {
  1148.       shdr = (elf_section_header_t *) ((uint8_t *) elf + ELF_GET_NUMERIC (elf, elf, section_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, section_header_item_size) * table_index); // quick access to section header
  1149.       name = &shstrtab[ELF_GET_NUMERIC (elf, shdr, name_offset)]; // peek at its name
  1150.       if (strcmp (name, section_name) == 0)
  1151.          return (shdr); // if found, return a pointer to this section header
  1152.    }
  1153.  
  1154.    return (NULL); // section not found
  1155. }
  1156.  
  1157.  
  1158. static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp)
  1159. {
  1160.    // writes a directory entry in the image filesystem file pointed to by fp (or fakes so if fp is NULL)
  1161.    // and return the number of bytes written (or that would have been written)
  1162.  
  1163.    static const uint8_t zeropad_buffer[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
  1164.  
  1165.    size_t datalen;
  1166.    size_t count;
  1167.  
  1168.    count = 0;
  1169.    if (fp != NULL)
  1170.       fwrite_or_die (&fsentry->header, 1, sizeof (fsentry->header), fp); // write the entry header (PACKED STRUCT)
  1171.    count += sizeof (fsentry->header);
  1172.    if (S_ISREG (fsentry->header.mode))
  1173.    {
  1174.       if (fp != NULL)
  1175.       {
  1176.          fwrite_or_die (&fsentry->u.file.offset, 1, sizeof (uint32_t), fp); // write offset
  1177.          fwrite_or_die (&fsentry->u.file.size,   1, sizeof (uint32_t), fp); // write size
  1178.       }
  1179.       count += 2 * sizeof (uint32_t);
  1180.       datalen = strlen (fsentry->u.file.path) + 1;
  1181.       if (fp != NULL)
  1182.          fwrite_or_die (fsentry->u.file.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash)
  1183.       count += datalen;
  1184.    }
  1185.    else if (S_ISDIR (fsentry->header.mode))
  1186.    {
  1187.       datalen = strlen (fsentry->u.dir.path) + 1;
  1188.       if (fp != NULL)
  1189.          fwrite_or_die (fsentry->u.dir.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash)
  1190.       count += datalen;
  1191.    }
  1192.    else if (S_ISLNK (fsentry->header.mode))
  1193.    {
  1194.       if (fp != NULL)
  1195.       {
  1196.          fwrite_or_die (&fsentry->u.symlink.sym_offset, 1, sizeof (uint16_t), fp); // write offset
  1197.          fwrite_or_die (&fsentry->u.symlink.sym_size,   1, sizeof (uint16_t), fp); // write size
  1198.       }
  1199.       count += 2 * sizeof (uint16_t);
  1200.       datalen = strlen (fsentry->u.symlink.path) + 1;
  1201.       if (fp != NULL)
  1202.          fwrite_or_die (fsentry->u.symlink.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash)
  1203.       count += datalen;
  1204.       datalen = strlen (fsentry->u.symlink.contents) + 1;
  1205.       if (fp != NULL)
  1206.          fwrite_or_die (fsentry->u.symlink.contents, 1, (size_t) datalen, fp); // write null-terminated symlink contents
  1207.       count += datalen;
  1208.    }
  1209.    else
  1210.    {
  1211.       if (fp != NULL)
  1212.       {
  1213.          fwrite_or_die (&fsentry->u.device.dev,  1, sizeof (uint32_t), fp); // write dev number
  1214.          fwrite_or_die (&fsentry->u.device.rdev, 1, sizeof (uint32_t), fp); // write rdev number
  1215.       }
  1216.       count += 2 * sizeof (uint32_t);
  1217.       datalen = strlen (fsentry->u.device.path) + 1;
  1218.       if (fp != NULL)
  1219.          fwrite_or_die (fsentry->u.device.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash)
  1220.       count += datalen;
  1221.    }
  1222.  
  1223.    if (count < fsentry->header.size)
  1224.    {
  1225.       if (fp != NULL)
  1226.          fwrite_or_die (zeropad_buffer, 1, fsentry->header.size - count, fp); // pad as necessary
  1227.       count += fsentry->header.size - count;
  1228.    }
  1229.    else if (count > fsentry->header.size)
  1230.    {
  1231.       fprintf (stderr, "ERROR: attempt to write invalid dirent (claimed size %zd, written size %zd). Aborting.\n", (size_t) fsentry->header.size, count);
  1232.       exit (1);
  1233.    }
  1234.  
  1235.    return (count);
  1236. }
  1237.  
  1238.  
  1239. static size_t add_fsentry (fsentry_t **fsentries, size_t *fsentry_count, parms_t *entry_parms, const char *stored_pathname, const char *buildhost_pathname)
  1240. {
  1241.    #define ADD_NAME_TO_STRINGTABLE(name,pstrtab,pstrtablen) do { \
  1242.       name_len = strlen ((name)) + 1; \
  1243.       reallocated_ptr = realloc (*(pstrtab), *(pstrtablen) + name_len); \
  1244.       WELLMANNERED_ASSERT (reallocated_ptr, "out of memory"); \
  1245.       *(pstrtab) = reallocated_ptr; \
  1246.       memcpy (&(*pstrtab)[*(pstrtablen)], (name), name_len); \
  1247.       *(pstrtablen) += name_len; \
  1248.    } while (0)
  1249.  
  1250.    static thread_local char candidate_pathname[1024];
  1251.    static int inode_count = 0; // will be preincremented each time this function is called
  1252.  
  1253.    const char *original_stored_pathname = NULL;
  1254.    const elf_dynamic_section_entry_t *dynamic_entry; // dynamic section entry
  1255.    const elf_section_header_t *shdr_dynstr; // dynamic strings
  1256.    const elf_section_header_t *shdr_dynamic; // dynamic section
  1257.    const elf_section_header_t *shdr;
  1258.    elf_section_header_t *new_shdr;
  1259.    size_t new_qnxinfo_shdr_offset;
  1260.    size_t new_debuglink_shdr_offset;
  1261.    size_t new_qnxusage_shdr_offset;
  1262.    size_t new_buildid_shdr_offset;
  1263.    size_t new_shstrtab_shdr_offset;
  1264.    elf_program_header_t *phdr;
  1265.    const char *canonical_dylib_name;
  1266.    const char *dynamic_strings; // strings table of the ".dynamic" section
  1267.    const char *last_dirsep;
  1268.    elf_header_t *elf;
  1269.    uint8_t *new_shdr_table = NULL; // mallocated
  1270.    size_t new_shdr_tablesize = 0;
  1271.    uint8_t *elfsection_qnxinfo_data = NULL; // mallocated
  1272.    size_t elfsection_qnxinfo_size = 0;
  1273.    uint8_t *elfsection_qnxusage_data = NULL; // mallocated
  1274.    size_t elfsection_qnxusage_size = 0;
  1275.    uint8_t *elfsection_debuglink_data = NULL; // mallocated
  1276.    size_t elfsection_debuglink_size = 0;
  1277.    uint8_t *elfsection_buildid_data = NULL; // mallocated
  1278.    size_t elfsection_buildid_size = 0;
  1279.    uint8_t *elfsection_shstrtab_data = NULL; // mallocated
  1280.    size_t elfsection_shstrtab_size = 0;
  1281.    char *resolved_pathname;
  1282.    void *reallocated_ptr;
  1283.    void *old_data;
  1284.    struct stat stat_buf;
  1285.    size_t new_shdrtable_offset;
  1286.    size_t end_padding_offset;
  1287.    size_t table_index;
  1288.    size_t table_count;
  1289.    size_t name_len;
  1290.    fsentry_t *fsentry;
  1291.  
  1292.    if (S_ISDIR (entry_parms->st_mode)) // are we storing a directory ?
  1293.    {
  1294.       fprintf (stderr, "directory: ino 0x%x uid %d gid %d mode 0%o path \"%s\"\n", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname);
  1295.    }
  1296.    else if (S_ISREG (entry_parms->st_mode)) // else are we storing a regular file ?
  1297.    {
  1298.       if (strcmp (stored_pathname, "/proc/boot/boot") == 0) // is it the kernel ?
  1299.       {
  1300.          // HACK: for now just consider the kernel as a binary blob
  1301.          // FIXME: reimplement properly
  1302.          sprintf (candidate_pathname, "%s/procnto-smp-instr", entry_parms->prefix); // fix the entry name
  1303.          stored_pathname = candidate_pathname;
  1304.          entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // procnto needs to have these flags stamped on the inode
  1305.          entry_parms->st_mode = S_IFREG | 0700; // procnto requires 0700 permissions
  1306.          image_kernel_ino = entry_parms->extra_ino_flags | (inode_count + 1);
  1307.       }
  1308.       else if (entry_parms->is_compiled_bootscript) // else is it a startup script ?
  1309.          image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
  1310.  
  1311.       // do we already know the data for this data blob ?
  1312.       if (entry_parms->data != NULL)
  1313.       {
  1314.          entry_parms->mtime = entry_parms->mtime_for_inline_files;
  1315.          fprintf (stderr, "file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" blob (len %zd)\n", entry_parms->extra_ino_flags | (inode_count + 1), entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->datalen);
  1316.       }
  1317.       else if (buildhost_pathname != NULL) // else was a source file pathname supplied ?
  1318.       {
  1319.          resolved_pathname = read_filecontents (buildhost_pathname, (entry_parms->search[0] != 0 ? entry_parms->search : MKIFS_PATH), &entry_parms->data, &entry_parms->datalen); // locate the file
  1320.          if (resolved_pathname == NULL)
  1321.          {
  1322.             fprintf (stderr, "fatal error: filesystem entry \"%s\" specified in \"%s\" line %d not found on build host: %s\n", buildhost_pathname, buildfile_pathname, lineno, strerror (errno));
  1323.             exit (1);
  1324.          }
  1325.          stat (resolved_pathname, &stat_buf); // can't fail
  1326.          if (entry_parms->mtime == UINT32_MAX)
  1327.             entry_parms->mtime = (uint32_t) stat_buf.st_mtime;
  1328.          fprintf (stderr, "file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" buildhost_file \"%s\" (len %zd)\n", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, buildhost_pathname, entry_parms->datalen);
  1329.       }
  1330.  
  1331.       // is the file we're storing an ELF file ?
  1332.       if ((entry_parms->datalen > 52) // file is big enough to contain an ELF header
  1333.           && ((elf = (elf_header_t *) entry_parms->data) != NULL) // cast (necessary true)
  1334.           && (memcmp (ELF_GET_STRING (elf, elf, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic
  1335.       {
  1336.          // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ?
  1337.          if ((ELF_GET_NUMERIC (elf, elf, type) == 3) && entry_parms->should_autosymlink_dylib)
  1338.          {
  1339.             // we need to find the SONAME of this library
  1340.             canonical_dylib_name = NULL;
  1341.  
  1342.             // locate the sections we need (the dynamic section and its strings table)
  1343.             shdr_dynamic = elf_get_section_header_by_name (elf, ".dynamic");
  1344.             shdr_dynstr = elf_get_section_header_by_name (elf, ".dynstr");
  1345.  
  1346.             // make sure we have both the dynamic section header and its own strings table header
  1347.             if ((shdr_dynamic != NULL) && (shdr_dynstr != NULL))
  1348.             {
  1349.                dynamic_strings = (char *) &entry_parms->data[ELF_GET_NUMERIC (elf, shdr_dynstr, file_offset)]; // quick access to dynamic sections strings table
  1350.  
  1351.                // walk through the dynamic section, look for the DT_SONAME entry
  1352.                for (dynamic_entry = (elf_dynamic_section_entry_t *) &entry_parms->data[ELF_GET_NUMERIC (elf, shdr_dynamic, file_offset)];
  1353.                     (ELF_GET_NUMERIC (elf, dynamic_entry, tag) != ELF_DT_NULL);
  1354.                     dynamic_entry = (elf_dynamic_section_entry_t *) ((uint8_t *) dynamic_entry + ELF_STRUCT_SIZE (elf, dynamic_entry)))
  1355.                   if (ELF_GET_NUMERIC (elf, dynamic_entry, tag) == ELF_DT_SONAME)
  1356.                   {
  1357.                      canonical_dylib_name = dynamic_strings + ELF_GET_NUMERIC (elf, dynamic_entry, value.as_integer);
  1358.                      break;
  1359.                   }
  1360.  
  1361.                // do we have it ?
  1362.                if ((canonical_dylib_name != NULL) && (canonical_dylib_name[0] != 0))
  1363.                {
  1364.                   sprintf (candidate_pathname, "%s/%s", entry_parms->prefix, canonical_dylib_name);
  1365.                   if (strcmp (candidate_pathname, stored_pathname) != 0) // claimed dylib name differs from passed name ?
  1366.                   {
  1367.                      original_stored_pathname = stored_pathname; // if so, remember to create a symlink here
  1368.                      stored_pathname = candidate_pathname;
  1369.                   }
  1370.                }
  1371.             }
  1372.          } // end if the file we're storing is a dylib
  1373.  
  1374.          // now strip this ELF file if necessary
  1375.          if (!(entry_parms->extra_ino_flags & IFS_INO_PROCESSED_ELF))
  1376.          {
  1377.             // NOTE: for each ELF file, mkifs
  1378.             // -> alters the program header table and offsets each p_addr (physical address) member by 0x1400000 plus the current file offset (this cannot be done right now, will need to be done once they are known)
  1379.             // -> throws away and reconstructs the sections table by keeping only the sections that are in the program header, and writes the section table at the start of the first thrown-away section
  1380.             // FIXME: what if a section thrown away is located between two program segments ? are they collapsed, moving everything one slot down ? this looks improbable...
  1381.  
  1382.             // reconstructed ELF:
  1383.             // ==== START OF FILE ====
  1384.             // ELF header
  1385.             // program header table
  1386.             //  (same sections, just p_addr offset down by first section's p_addr)
  1387.             // section data 5 (named ".note.gnu.build-id")
  1388.             //  "............GNU....ZY.....c.o..l"
  1389.             // PROGRAM
  1390.             // sections table
  1391.             // + section 1: ALL ZEROES
  1392.             // + section 2: fileoffs 0x21a8 size 0xfd --> "QNX_info" --> QNX binary description: "NAME=pci_debug2.so.3.0\nDESCRIPTION=PCI Server System Debug Module\nDATE=2023/11/19-10:01:13-EST\nSTATE=lookup\nHOST=docker-n1.bts.rim.net\nUSER=builder\nVERSION=QNXOS_main\nTAGID=QNXOS_800-135\nPACKAGE=com.qnx.qnx800.target.pci.debug/3.0.0.00135T202311191043L\n"
  1393.             // + section 3: fileoffs 0x22a5 size 0x1c --> ".gnu_debuglink" --> indicates the debug file (and a possible checksum?): "pci_debug2.so.3.0.sym" "\0\0\0" "VX2p"
  1394.             // + section 4: fileoffs 0x22c1 size 0x2ad --> "QNX_usage" --> HELP TEXT: "\n-------------------------------------------------------------------------------\n%C\n\nThis module implements debug logging for all PCI server modules. It is\nincluded by setting the environment variable PCI_DEBUG_MODULE and uses\nthe slogger2 APIs.\nNOTE:.On systems which support slogger2, you are encouraged to use this module.instead of pci_debug.so...Release History.---------------..3.0 - This module is functionally equivalent to the previous 2.x version.      however it is incompatible with all pre v3.x PCI components..2.1 - fixes a bug whereby if slogger2 is not running and the PCI_DEBUG_MODULE.      environment variable is set, the client will SIGSEGV..2.0 - initial release.."
  1395.             // + section 5: fileoffs 0x190 size 0x32 --> ".note.gnu.build-id" --> GNU build ID
  1396.             // + section 6: fileoffs 0x256e size 0x40 --> ".shstrtab" --> sections names strings table
  1397.             // section data 2 (named "QNX_info")
  1398.             //  (QNX binary description)
  1399.             // section data 3 (named ".gnu_debuglink")
  1400.             //  (debug file)
  1401.             // section data 4 (named "QNX_usage")
  1402.             //  (help text)
  1403.             // section data 6 (named ".shstrtab")
  1404.             //  "\0"
  1405.             //  ".shstrtab\0"
  1406.             //  "QNX_info\0"
  1407.             //  ".gnu_debuglink\0"
  1408.             //  "QNX_usage\0"
  1409.             //  ".note.gnu.build-id\0"
  1410.             // ==== END OF FILE ====
  1411.  
  1412.             // task 1: alter the program header table, and measure the farthest offset known by this table where we'll write the reconstructed section headers table
  1413.  
  1414.             //first_offset = SIZE_MAX;
  1415.             new_shdrtable_offset = 0;
  1416.             table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len);
  1417.             for (table_index = 0; table_index < table_count; table_index++)
  1418.             {
  1419.                phdr = (elf_program_header_t *) &entry_parms->data[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header
  1420.                //if (first_offset == SIZE_MAX)
  1421.                //   first_offset = ELF_GET_NUMERIC (elf, phdr, physical_addr);
  1422.                if (ELF_GET_NUMERIC (elf, phdr, file_offset) + ELF_GET_NUMERIC (elf, phdr, size_in_file) > new_shdrtable_offset)
  1423.                   new_shdrtable_offset = ELF_GET_NUMERIC (elf, phdr, file_offset) + ELF_GET_NUMERIC (elf, phdr, size_in_file);
  1424. //               ELF_SET_NUMERIC (elf, phdr, physical_addr, ELF_GET_NUMERIC (elf, phdr, physical_addr) + (0x1400000 + ));
  1425.             }
  1426.  
  1427.             // task 2: re-create the section header table
  1428.  
  1429.             elfsection_shstrtab_data = malloc (1); // initialize an empty section headers strings table
  1430.             WELLMANNERED_ASSERT (elfsection_shstrtab_data, "out of memory");
  1431.             elfsection_shstrtab_data[0] = 0;
  1432.             elfsection_shstrtab_size = 1;
  1433.             ADD_NAME_TO_STRINGTABLE (".shstrtab", &elfsection_shstrtab_data, &elfsection_shstrtab_size);
  1434.  
  1435.             reallocated_ptr = realloc (new_shdr_table, new_shdr_tablesize + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more
  1436.             WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
  1437.             new_shdr_table = reallocated_ptr; // reallocation succeeded, save the new pointer
  1438.             memset (&new_shdr_table[new_shdr_tablesize], 0, ELF_STRUCT_SIZE (elf, shdr)); // the first section header is always zerofilled
  1439.             new_shdr_tablesize += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now
  1440.             if ((shdr = elf_get_section_header_by_name (elf, "QNX_info")) != NULL)
  1441.             {
  1442.                if (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset) // if this section needs to be moved around, have a copy of it
  1443.                {
  1444.                   elfsection_qnxinfo_size = ELF_GET_NUMERIC (elf, shdr, size);
  1445.                   elfsection_qnxinfo_data = malloc (elfsection_qnxinfo_size);
  1446.                   WELLMANNERED_ASSERT (elfsection_qnxinfo_data, "out of memory");
  1447.                   memcpy (elfsection_qnxinfo_data, &entry_parms->data[ELF_GET_NUMERIC (elf, shdr, file_offset)], elfsection_qnxinfo_size);
  1448.                }
  1449.                reallocated_ptr = realloc (new_shdr_table, new_shdr_tablesize + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more
  1450.                WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
  1451.                new_shdr_table = reallocated_ptr; // reallocation succeeded, save the new pointer
  1452.                new_qnxinfo_shdr_offset = new_shdr_tablesize; // remember the new offset of this section header
  1453.                new_shdr_tablesize += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now
  1454.  
  1455.                new_shdr = (elf_section_header_t *) &new_shdr_table[new_qnxinfo_shdr_offset]; // now populate this section header
  1456.                ADD_NAME_TO_STRINGTABLE ("QNX_info", &elfsection_shstrtab_data, &elfsection_shstrtab_size);
  1457.                ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in ("QNX_info", elfsection_shstrtab_data, elfsection_shstrtab_size)); // update the relative offset of the section name
  1458.                ELF_SET_NUMERIC (elf, new_shdr, type,         ELF_GET_NUMERIC (elf, shdr, type)); // duplicate section type
  1459.                ELF_SET_NUMERIC (elf, new_shdr, flags,        ELF_GET_NUMERIC (elf, shdr, flags)); // duplicate section flags
  1460.                ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, ELF_GET_NUMERIC (elf, shdr, virtual_addr)); // duplicate section virtual address
  1461.                ELF_SET_NUMERIC (elf, new_shdr, file_offset, (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset ? WILL_BE_FILLED_LATER : ELF_GET_NUMERIC (elf, shdr, file_offset))); // duplicate section offset only if it doesn't move
  1462.                ELF_SET_NUMERIC (elf, new_shdr, size,         ELF_GET_NUMERIC (elf, shdr, size)); // duplicate section size
  1463.                ELF_SET_NUMERIC (elf, new_shdr, linked_index, ELF_GET_NUMERIC (elf, shdr, linked_index)); // duplicate section linked index (FIXME: may be wrong now!)
  1464.                ELF_SET_NUMERIC (elf, new_shdr, info,         ELF_GET_NUMERIC (elf, shdr, info)); // duplicate section info
  1465.                ELF_SET_NUMERIC (elf, new_shdr, alignment,    ELF_GET_NUMERIC (elf, shdr, alignment)); // duplicate section alignment
  1466.                ELF_SET_NUMERIC (elf, new_shdr, entry_size,   ELF_GET_NUMERIC (elf, shdr, entry_size)); // duplicate section entry size
  1467.             }
  1468.             if ((shdr = elf_get_section_header_by_name (elf, ".gnu_debuglink")) != NULL)
  1469.             {
  1470.                if (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset) // if this section needs to be moved around, have a copy of it
  1471.                {
  1472.                   elfsection_debuglink_size = ELF_GET_NUMERIC (elf, shdr, size);
  1473.                   elfsection_debuglink_data = malloc (elfsection_debuglink_size);
  1474.                   WELLMANNERED_ASSERT (elfsection_debuglink_data, "out of memory");
  1475.                   memcpy (elfsection_debuglink_data, &entry_parms->data[ELF_GET_NUMERIC (elf, shdr, file_offset)], elfsection_debuglink_size);
  1476.                }
  1477.                reallocated_ptr = realloc (new_shdr_table, new_shdr_tablesize + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more
  1478.                WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
  1479.                new_shdr_table = reallocated_ptr; // reallocation succeeded, save the new pointer
  1480.                new_debuglink_shdr_offset = new_shdr_tablesize; // remember the new offset of this section header
  1481.                new_shdr_tablesize += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now
  1482.  
  1483.                new_shdr = (elf_section_header_t *) &new_shdr_table[new_debuglink_shdr_offset]; // now populate this section header
  1484.                ADD_NAME_TO_STRINGTABLE (".gnu_debuglink", &elfsection_shstrtab_data, &elfsection_shstrtab_size);
  1485.                ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in (".gnu_debuglink", elfsection_shstrtab_data, elfsection_shstrtab_size)); // update the relative offset of the section name
  1486.                ELF_SET_NUMERIC (elf, new_shdr, type,         ELF_GET_NUMERIC (elf, shdr, type)); // duplicate section type
  1487.                ELF_SET_NUMERIC (elf, new_shdr, flags,        ELF_GET_NUMERIC (elf, shdr, flags)); // duplicate section flags
  1488.                ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, ELF_GET_NUMERIC (elf, shdr, virtual_addr)); // duplicate section virtual address
  1489.                ELF_SET_NUMERIC (elf, new_shdr, file_offset, (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset ? WILL_BE_FILLED_LATER : ELF_GET_NUMERIC (elf, shdr, file_offset))); // duplicate section offset only if it doesn't move
  1490.                ELF_SET_NUMERIC (elf, new_shdr, size,         ELF_GET_NUMERIC (elf, shdr, size)); // duplicate section size
  1491.                ELF_SET_NUMERIC (elf, new_shdr, linked_index, ELF_GET_NUMERIC (elf, shdr, linked_index)); // duplicate section linked index (FIXME: may be wrong now!)
  1492.                ELF_SET_NUMERIC (elf, new_shdr, info,         ELF_GET_NUMERIC (elf, shdr, info)); // duplicate section info
  1493.                ELF_SET_NUMERIC (elf, new_shdr, alignment,    ELF_GET_NUMERIC (elf, shdr, alignment)); // duplicate section alignment
  1494.                ELF_SET_NUMERIC (elf, new_shdr, entry_size,   ELF_GET_NUMERIC (elf, shdr, entry_size)); // duplicate section entry size
  1495.             }
  1496.             if ((shdr = elf_get_section_header_by_name (elf, "QNX_usage")) != NULL)
  1497.             {
  1498.                if (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset) // if this section needs to be moved around, have a copy of it
  1499.                {
  1500.                   elfsection_qnxusage_size = ELF_GET_NUMERIC (elf, shdr, size);
  1501.                   elfsection_qnxusage_data = malloc (elfsection_qnxusage_size);
  1502.                   WELLMANNERED_ASSERT (elfsection_qnxusage_data, "out of memory");
  1503.                   memcpy (elfsection_qnxusage_data, &entry_parms->data[ELF_GET_NUMERIC (elf, shdr, file_offset)], elfsection_qnxusage_size);
  1504.                }
  1505.                reallocated_ptr = realloc (new_shdr_table, new_shdr_tablesize + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more
  1506.                WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
  1507.                new_shdr_table = reallocated_ptr; // reallocation succeeded, save the new pointer
  1508.                new_qnxusage_shdr_offset = new_shdr_tablesize; // remember the new offset of this section header
  1509.                new_shdr_tablesize += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now
  1510.  
  1511.                new_shdr = (elf_section_header_t *) &new_shdr_table[new_qnxusage_shdr_offset]; // now populate this section header
  1512.                ADD_NAME_TO_STRINGTABLE ("QNX_usage", &elfsection_shstrtab_data, &elfsection_shstrtab_size);
  1513.                ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in ("QNX_usage", elfsection_shstrtab_data, elfsection_shstrtab_size)); // update the relative offset of the section name
  1514.                ELF_SET_NUMERIC (elf, new_shdr, type,         ELF_GET_NUMERIC (elf, shdr, type)); // duplicate section type
  1515.                ELF_SET_NUMERIC (elf, new_shdr, flags,        ELF_GET_NUMERIC (elf, shdr, flags)); // duplicate section flags
  1516.                ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, ELF_GET_NUMERIC (elf, shdr, virtual_addr)); // duplicate section virtual address
  1517.                ELF_SET_NUMERIC (elf, new_shdr, file_offset, (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset ? WILL_BE_FILLED_LATER : ELF_GET_NUMERIC (elf, shdr, file_offset))); // duplicate section offset only if it doesn't move
  1518.                ELF_SET_NUMERIC (elf, new_shdr, size,         ELF_GET_NUMERIC (elf, shdr, size)); // duplicate section size
  1519.                ELF_SET_NUMERIC (elf, new_shdr, linked_index, ELF_GET_NUMERIC (elf, shdr, linked_index)); // duplicate section linked index (FIXME: may be wrong now!)
  1520.                ELF_SET_NUMERIC (elf, new_shdr, info,         ELF_GET_NUMERIC (elf, shdr, info)); // duplicate section info
  1521.                ELF_SET_NUMERIC (elf, new_shdr, alignment,    ELF_GET_NUMERIC (elf, shdr, alignment)); // duplicate section alignment
  1522.                ELF_SET_NUMERIC (elf, new_shdr, entry_size,   ELF_GET_NUMERIC (elf, shdr, entry_size)); // duplicate section entry size
  1523.             }
  1524.             if ((shdr = elf_get_section_header_by_name (elf, ".note.gnu.build-id")) != NULL)
  1525.             {
  1526.                if (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset) // if this section needs to be moved around, have a copy of it
  1527.                {
  1528.                   elfsection_buildid_size = ELF_GET_NUMERIC (elf, shdr, size);
  1529.                   elfsection_buildid_data = malloc (elfsection_buildid_size);
  1530.                   WELLMANNERED_ASSERT (elfsection_buildid_data, "out of memory");
  1531.                   memcpy (elfsection_buildid_data, &entry_parms->data[ELF_GET_NUMERIC (elf, shdr, file_offset)], elfsection_buildid_size);
  1532.                }
  1533.                reallocated_ptr = realloc (new_shdr_table, new_shdr_tablesize + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more
  1534.                WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
  1535.                new_shdr_table = reallocated_ptr; // reallocation succeeded, save the new pointer
  1536.                new_buildid_shdr_offset = new_shdr_tablesize; // remember the new offset of this section header
  1537.                new_shdr_tablesize += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now
  1538.  
  1539.                new_shdr = (elf_section_header_t *) &new_shdr_table[new_buildid_shdr_offset]; // now populate this section header
  1540.                ADD_NAME_TO_STRINGTABLE (".note.gnu.build-id", &elfsection_shstrtab_data, &elfsection_shstrtab_size);
  1541.                ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in (".note.gnu.build-id", elfsection_shstrtab_data, elfsection_shstrtab_size)); // update the relative offset of the section name
  1542.                ELF_SET_NUMERIC (elf, new_shdr, type,         ELF_GET_NUMERIC (elf, shdr, type)); // duplicate section type
  1543.                ELF_SET_NUMERIC (elf, new_shdr, flags,        ELF_GET_NUMERIC (elf, shdr, flags)); // duplicate section flags
  1544.                ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, ELF_GET_NUMERIC (elf, shdr, virtual_addr)); // duplicate section virtual address
  1545.                ELF_SET_NUMERIC (elf, new_shdr, file_offset, (ELF_GET_NUMERIC (elf, shdr, file_offset) > new_shdrtable_offset ? WILL_BE_FILLED_LATER : ELF_GET_NUMERIC (elf, shdr, file_offset))); // duplicate section offset only if it doesn't move
  1546.                ELF_SET_NUMERIC (elf, new_shdr, size,         ELF_GET_NUMERIC (elf, shdr, size)); // duplicate section size
  1547.                ELF_SET_NUMERIC (elf, new_shdr, linked_index, ELF_GET_NUMERIC (elf, shdr, linked_index)); // duplicate section linked index (FIXME: may be wrong now!)
  1548.                ELF_SET_NUMERIC (elf, new_shdr, info,         ELF_GET_NUMERIC (elf, shdr, info)); // duplicate section info
  1549.                ELF_SET_NUMERIC (elf, new_shdr, alignment,    ELF_GET_NUMERIC (elf, shdr, alignment)); // duplicate section alignment
  1550.                ELF_SET_NUMERIC (elf, new_shdr, entry_size,   ELF_GET_NUMERIC (elf, shdr, entry_size)); // duplicate section entry size
  1551.             }
  1552.             reallocated_ptr = realloc (new_shdr_table, new_shdr_tablesize + ELF_STRUCT_SIZE (elf, shdr)); // grow our section headers table to have one entry more
  1553.             WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
  1554.             new_shdr_table = reallocated_ptr; // reallocation succeeded, save the new pointer
  1555.             new_shstrtab_shdr_offset = new_shdr_tablesize; // remember the new offset of this section header
  1556.             new_shdr_tablesize += ELF_STRUCT_SIZE (elf, shdr); // and remember how bigger the section headers table is now
  1557.  
  1558.             new_shdr = (elf_section_header_t *) &new_shdr_table[new_shstrtab_shdr_offset]; // now populate this section header
  1559.             ELF_SET_NUMERIC (elf, new_shdr, name_offset, relative_offset_of_in (".shstrtab", elfsection_shstrtab_data, elfsection_shstrtab_size)); // update the relative offset of the section name
  1560.             ELF_SET_NUMERIC (elf, new_shdr, type,         ELF_SECTIONTYPE_STRINGTABLE); // section type (SHT_STRTAB)
  1561.             ELF_SET_NUMERIC (elf, new_shdr, flags,        0); // section flags (we could set SHF_STRINGS i.e. 0x20 here, but mkifs does not, so mimic that)
  1562.             ELF_SET_NUMERIC (elf, new_shdr, virtual_addr, 0); // this section does not need to be mapped
  1563.             ELF_SET_NUMERIC (elf, new_shdr, file_offset, WILL_BE_FILLED_LATER); // FIXME
  1564.             ELF_SET_NUMERIC (elf, new_shdr, size,         elfsection_shstrtab_size); // section size
  1565.             ELF_SET_NUMERIC (elf, new_shdr, linked_index, 0); // this section is not linked to any other
  1566.             ELF_SET_NUMERIC (elf, new_shdr, info,         0); // this section has no additional info
  1567.             ELF_SET_NUMERIC (elf, new_shdr, alignment,    1); // this section is byte-aligned
  1568.             ELF_SET_NUMERIC (elf, new_shdr, entry_size,   0); // this section is not a table, so entry_size is zero
  1569.  
  1570.             // jump over the new section headers table and write the sections that need to be relocated after the section headers table
  1571.             entry_parms->datalen = new_shdrtable_offset + new_shdr_tablesize; // assume there are no sections beyond the section headers table until known otherwise
  1572.             if (elfsection_qnxinfo_data != NULL)
  1573.             {
  1574.                memcpy (&entry_parms->data[entry_parms->datalen], elfsection_qnxinfo_data, elfsection_qnxinfo_size); // write section in place
  1575.                free (elfsection_qnxinfo_data); // free it
  1576.                new_shdr = (elf_section_header_t *) &new_shdr_table[new_qnxinfo_shdr_offset]; // now fix this section header
  1577.                ELF_SET_NUMERIC (elf, new_shdr, file_offset, entry_parms->datalen); // fix section offset in the new section headers table
  1578.                entry_parms->datalen += elfsection_qnxinfo_size; // update new ELF file length
  1579.             }
  1580.             if (elfsection_debuglink_data != NULL)
  1581.             {
  1582.                memcpy (&entry_parms->data[entry_parms->datalen], elfsection_debuglink_data, elfsection_debuglink_size); // write section in place
  1583.                free (elfsection_debuglink_data); // free it
  1584.                new_shdr = (elf_section_header_t *) &new_shdr_table[new_debuglink_shdr_offset]; // now fix this section header
  1585.                ELF_SET_NUMERIC (elf, new_shdr, file_offset, entry_parms->datalen); // fix section offset in the new section headers table
  1586.                entry_parms->datalen += elfsection_debuglink_size; // update new ELF file length
  1587.             }
  1588.             if (elfsection_qnxusage_data != NULL)
  1589.             {
  1590.                memcpy (&entry_parms->data[entry_parms->datalen], elfsection_qnxusage_data, elfsection_qnxusage_size); // write section in place
  1591.                free (elfsection_qnxusage_data); // free it
  1592.                new_shdr = (elf_section_header_t *) &new_shdr_table[new_qnxusage_shdr_offset]; // now fix this section header
  1593.                ELF_SET_NUMERIC (elf, new_shdr, file_offset, entry_parms->datalen); // fix section offset in the new section headers table
  1594.                entry_parms->datalen += elfsection_qnxusage_size; // update new ELF file length
  1595.             }
  1596.             if (elfsection_buildid_data != NULL)
  1597.             {
  1598.                memcpy (&entry_parms->data[entry_parms->datalen], elfsection_buildid_data, elfsection_buildid_size); // write section in place
  1599.                free (elfsection_buildid_data); // free it
  1600.                new_shdr = (elf_section_header_t *) &new_shdr_table[new_buildid_shdr_offset]; // now fix this section header
  1601.                ELF_SET_NUMERIC (elf, new_shdr, file_offset, entry_parms->datalen); // fix section offset in the new section headers table
  1602.                entry_parms->datalen += elfsection_buildid_size; // update new ELF file length
  1603.             }
  1604.             if (elfsection_shstrtab_data != NULL)
  1605.             {
  1606.                memcpy (&entry_parms->data[entry_parms->datalen], elfsection_shstrtab_data, elfsection_shstrtab_size); // write section header strings table section in place
  1607.                free (elfsection_shstrtab_data);
  1608.                new_shdr = (elf_section_header_t *) &new_shdr_table[new_shstrtab_shdr_offset]; // now fix this section header
  1609.                ELF_SET_NUMERIC (elf, new_shdr, file_offset, entry_parms->datalen); // fix section offset in the new section headers table
  1610.                entry_parms->datalen += elfsection_shstrtab_size; // update new ELF file length
  1611.             }
  1612.  
  1613.             // now write the section headers table
  1614.             fprintf (stderr, "rewriting section headers table at offset 0x%zd\n", new_shdrtable_offset);
  1615.             memcpy (&entry_parms->data[new_shdrtable_offset], new_shdr_table, new_shdr_tablesize);
  1616.             free (new_shdr_table); // free it
  1617.  
  1618.             // and finally fix the ELF header
  1619.             ELF_SET_NUMERIC (elf, elf, section_header_table_offset, new_shdrtable_offset);
  1620.             ELF_SET_NUMERIC (elf, elf, section_header_table_len, new_shdr_tablesize / ELF_STRUCT_SIZE (elf, shdr));
  1621.             ELF_SET_NUMERIC (elf, elf, section_header_names_idx, new_shdr_tablesize / ELF_STRUCT_SIZE (elf, shdr) - 1); // the section headers strings table is the last section
  1622.  
  1623.             // align size with page size (4096 on x86, 16k on ARM)
  1624.             end_padding_offset = entry_parms->datalen;
  1625.             if (ELF_GET_NUMERIC (elf, elf, instruction_set) == ELF_MACHINE_X86_64)
  1626.                entry_parms->datalen = ROUND_TO_UPPER_MULTIPLE (end_padding_offset, 4 * 1024); // 4 kb pages on Intel processors
  1627.             else if (ELF_GET_NUMERIC (elf, elf, instruction_set) == ELF_MACHINE_AARCH64)
  1628.                entry_parms->datalen = ROUND_TO_UPPER_MULTIPLE (end_padding_offset, 16 * 1024); // 16 kb pages on ARM64
  1629.             else
  1630.             {
  1631.                fprintf (stderr, "fatal error: this ELF file \"%s\" does not belong to an architecture supported by ifstool (neither x86_64, nor aarch64)\n", stored_pathname);
  1632.                exit (1);
  1633.             }
  1634.             memset (&entry_parms->data[end_padding_offset], 0, entry_parms->datalen - end_padding_offset); // zerofill
  1635.  
  1636.             entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF; // mark this inode as a preprocessed ELF file
  1637.          } // end if the file is not yet a processed ELF
  1638.       } // end if the file we're storing is an ELF file
  1639.    }
  1640.    else if (S_ISLNK (entry_parms->st_mode)) // else are we storing a symbolic link ?
  1641.       fprintf (stderr, "symlink: ino 0x%x uid %d gid %d mode 0%o path \"%s\" -> \"%s\"\n", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data);
  1642.    else // we must be storing a FIFO
  1643.    {
  1644.       if (strchr (entry_parms->data, ':') == NULL)
  1645.       {
  1646.          fprintf (stderr, "fatal error: device entry \"%s\" malformed (no 'dev:rdev' pair)\n", stored_pathname);
  1647.          exit (1);
  1648.       }
  1649.       fprintf (stderr, "fifo: ino 0x%x uid %d gid %d mode 0%o path \"%s\" dev rdev %s)\n", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data);
  1650.    }
  1651.  
  1652.    // grow filesystem entries array to hold one more slot
  1653.    reallocated_ptr = realloc (*fsentries, (*fsentry_count + 1) * sizeof (fsentry_t)); // attempt to reallocate
  1654.    if (reallocated_ptr == NULL)
  1655.    {
  1656.       fprintf (stderr, "fatal error: out of memory\n");
  1657.       exit (1);
  1658.    }
  1659.    *fsentries = reallocated_ptr; // save reallocated pointer
  1660.    fsentry = &(*fsentries)[*fsentry_count]; // quick access to fs entry slot
  1661.    //fsentry->header.size = 0; // will be filled once we know it
  1662.    fsentry->header.extattr_offset = 0;
  1663.    fsentry->header.ino = entry_parms->extra_ino_flags | (++inode_count);
  1664.    fsentry->header.mode = entry_parms->st_mode;
  1665.    fsentry->header.gid = entry_parms->gid;
  1666.    fsentry->header.uid = entry_parms->uid;
  1667.    fsentry->header.mtime = (entry_parms->mtime == UINT32_MAX ? (uint32_t) time (NULL) : entry_parms->mtime);
  1668.    if (S_ISDIR (entry_parms->st_mode))
  1669.    {
  1670.       fsentry->u.dir.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
  1671.       fsentry->header.size = (uint16_t) ROUND_TO_UPPER_MULTIPLE (sizeof (fsentry->header) + strlen (fsentry->u.dir.path) + 1, image_align); // now we can set the size
  1672.       fsentry->UNSAVED_was_data_written = true; // no data to save
  1673.    }
  1674.    else if (S_ISREG (entry_parms->st_mode))
  1675.    {
  1676.       fsentry->u.file.offset = WILL_BE_FILLED_LATER; // will be filled later in main() when the file's data blob will be written to the output file
  1677.       fsentry->u.file.size = (uint32_t) entry_parms->datalen;
  1678.       fsentry->u.file.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
  1679.       fsentry->u.file.UNSAVED_databuf = malloc (entry_parms->datalen);
  1680.       WELLMANNERED_ASSERT (fsentry->u.file.UNSAVED_databuf, "out of memory");
  1681.       memcpy (fsentry->u.file.UNSAVED_databuf, entry_parms->data, entry_parms->datalen);
  1682.       fsentry->header.size = (uint16_t) ROUND_TO_UPPER_MULTIPLE (sizeof (fsentry->header) + sizeof (uint32_t) + sizeof (uint32_t) + strlen (fsentry->u.file.path) + 1, image_align); // now we can set the size
  1683.       fsentry->UNSAVED_was_data_written = false; // there *IS* data to save
  1684.    }
  1685.    else if (S_ISLNK (entry_parms->st_mode))
  1686.    {
  1687.       fsentry->u.symlink.sym_offset = (uint16_t) (strlen (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname) + 1);
  1688.       fsentry->u.symlink.sym_size = (uint16_t) entry_parms->datalen;
  1689.       fsentry->u.symlink.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
  1690.       fsentry->u.symlink.contents = strdup (entry_parms->data);
  1691.       WELLMANNERED_ASSERT (fsentry->u.symlink.contents, "out of memory");
  1692.       fsentry->header.size = (uint16_t) ROUND_TO_UPPER_MULTIPLE (sizeof (fsentry->header) + sizeof (uint16_t) + sizeof (uint16_t) + (size_t) fsentry->u.symlink.sym_offset + fsentry->u.symlink.sym_size + 1, image_align); // now we can set the size
  1693.       fsentry->UNSAVED_was_data_written = true; // no data to save
  1694.    }
  1695.    else // necessarily a device node
  1696.    {
  1697.       fsentry->u.device.dev  = strtol (entry_parms->data, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
  1698.       fsentry->u.device.rdev = strtol (strchr (entry_parms->data, ':') + 1, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
  1699.       fsentry->u.device.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
  1700.       fsentry->header.size = (uint16_t) ROUND_TO_UPPER_MULTIPLE (sizeof (fsentry->header) + sizeof (uint32_t) + sizeof (uint32_t) + strlen (fsentry->u.device.path), image_align); // now we can set the size
  1701.       fsentry->UNSAVED_was_data_written = true; // no data to save
  1702.    }
  1703.    (*fsentry_count)++;
  1704.  
  1705.    // should we also add a symlink to this entry ? (in case we stored a dylib file under its canonical name)
  1706.    if (original_stored_pathname != NULL)
  1707.    {
  1708.       entry_parms->is_compiled_bootscript = false;
  1709.       entry_parms->should_autosymlink_dylib = false;
  1710.       entry_parms->should_follow_symlinks = false;
  1711.       entry_parms->st_mode = S_IFLNK | 0777; // NOTE: mkifs stores symlink permissions as rwxrwxrwx !
  1712.       entry_parms->extra_ino_flags = (fsentry->header.ino & (IFS_INO_PROCESSED_ELF | IFS_INO_RUNONCE_ELF | IFS_INO_BOOTSTRAP_EXE)); // preserve target's inode flags
  1713.       last_dirsep = strrchr (stored_pathname, '/');
  1714.       old_data = entry_parms->data; // backup previous data pointer
  1715.       entry_parms->data = (uint8_t *) (last_dirsep == NULL ? stored_pathname : last_dirsep + 1); // store symlink target in dirent data
  1716.       entry_parms->datalen = strlen (entry_parms->data);
  1717.       add_fsentry (fsentries, fsentry_count, entry_parms, original_stored_pathname, NULL);
  1718.       entry_parms->data = old_data; // restore previous data pointer so that it can be freed normally
  1719.    }
  1720.  
  1721.    return (*fsentry_count);
  1722. }
  1723.  
  1724.  
  1725. static int fsentry_compare_pathnames_cb (const void *a, const void *b)
  1726. {
  1727.    // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname
  1728.  
  1729.    const fsentry_t *entry_a = (const fsentry_t *) a;
  1730.    const fsentry_t *entry_b = (const fsentry_t *) b;
  1731.    const char *pathname_a = (S_ISDIR (entry_a->header.mode) ? entry_a->u.dir.path : (S_ISREG (entry_a->header.mode) ? entry_a->u.file.path : (S_ISLNK (entry_a->header.mode) ? entry_a->u.symlink.path : entry_a->u.device.path)));
  1732.    const char *pathname_b = (S_ISDIR (entry_b->header.mode) ? entry_b->u.dir.path : (S_ISREG (entry_b->header.mode) ? entry_b->u.file.path : (S_ISLNK (entry_b->header.mode) ? entry_b->u.symlink.path : entry_b->u.device.path)));
  1733.    return (strcmp (pathname_a, pathname_b));
  1734. }
  1735.  
  1736.  
  1737. static void update_MKIFS_PATH (const char *processor)
  1738. {
  1739.    // updates the value of MKIFS_PATH according to the passed processor name string, unless an environment variable already defines it
  1740.  
  1741.    char processor_base[16];
  1742.    size_t data_len;
  1743.    char *envvar;
  1744.    char *token;
  1745.    
  1746.    envvar = getenv ("MKIFS_PATH"); // look in the environment first, and construct a default one if not supplied
  1747.    if (envvar != NULL)
  1748.       MKIFS_PATH = envvar; // if envvar is present, set MKIFS_PATH to point to it
  1749.    else // envvar not present
  1750.    {
  1751.       if (MKIFS_PATH != NULL)
  1752.          free (MKIFS_PATH); // free any MKIFS_PATH that we constructed earlier
  1753.  
  1754.       strcpy (processor_base, processor); // construct PROCESSOR_BASE
  1755.       token = strchr (processor_base, '-');
  1756.       if (token != NULL)
  1757.          *token = 0; // split anything from the first dash onwards
  1758.       data_len = strlen (processor_base);
  1759.       if ((data_len > 2) && ((processor_base[data_len - 2] == 'b') || (processor_base[data_len - 2] == 'l')) && (processor_base[data_len - 1] == 'e'))
  1760.          processor_base[data_len - 2] = 0; // if it ends with "le" or "be", strip that too
  1761.  
  1762.       MKIFS_PATH = malloc (10 * MAXPATHLEN); // construct a default MKIFS_PATH now
  1763.       WELLMANNERED_ASSERT (MKIFS_PATH, "out of memory");
  1764.       sprintf (MKIFS_PATH, "." PATH_SEP_STR "%s/%s/sbin" PATH_SEP_STR "%s/%s/usr/sbin" PATH_SEP_STR "%s/%s/boot/sys" PATH_SEP_STR "%s/%s/boot/sys" PATH_SEP_STR "%s/%s/bin" PATH_SEP_STR "%s/%s/usr/bin" PATH_SEP_STR "%s/%s/lib" PATH_SEP_STR "%s/%s/lib/dll" PATH_SEP_STR "%s/%s/usr/lib", // use a platform-specific character as path separator
  1765.                QNX_TARGET, processor,
  1766.                QNX_TARGET, processor,
  1767.                QNX_TARGET, processor,
  1768.                QNX_TARGET, processor_base,
  1769.                QNX_TARGET, processor,
  1770.                QNX_TARGET, processor,
  1771.                QNX_TARGET, processor,
  1772.                QNX_TARGET, processor,
  1773.                QNX_TARGET, processor);
  1774.    }
  1775.  
  1776.    return;
  1777. }
  1778.  
  1779.  
  1780. int main (int argc, char **argv)
  1781. {
  1782.    // program entrypoint
  1783.  
  1784.    #define PAD_OUTFILE_TO(val) do { curr_offset = ftell (fp); while (curr_offset < (val)) { putc (0, fp); curr_offset++; } } while (0)
  1785.  
  1786.    static startup_header_t startup_header = { 0 }; // output IFS's startup header
  1787.    static startup_trailer_v2_t startup_trailer = { 0 }; // output IFS's startup trailer (version 2, with SHA-512 checksum and int32 checksum)
  1788.    static image_header_t image_header = { 0 }; // output IFS's imagefs header
  1789.    static image_trailer_v2_t image_trailer = { 0 }; // output IFS's imagefs trailer (version 2, with SHA-512 checksum and int32 checksum)
  1790.    static fsentry_t *fsentries = NULL; // output IFS's filesystem entries
  1791.    static size_t fsentry_count = 0; // number of entries in the IFS filesystem
  1792.    static parms_t default_parms = { // default parameters for a filesystem entry
  1793.       .dperms = 0755,
  1794.       .perms = 0644,
  1795.       .uid = 0,
  1796.       .gid = 0,
  1797.       .st_mode = S_IFREG,
  1798.       .mtime = UINT32_MAX,
  1799.       .mtime_for_inline_files = UINT32_MAX,
  1800.       .prefix = "/proc/boot",
  1801.       .should_follow_symlinks = true, // [+|-followlink]
  1802.       .should_autosymlink_dylib = true, // [+|-autolink]
  1803.       .is_compiled_bootscript = false, // [+|-script]
  1804.       .extra_ino_flags = 0,
  1805.       .search = "",
  1806.       .data = NULL,
  1807.       .datalen = 0
  1808.    };
  1809.    static parms_t entry_parms = { 0 }; // current parameters for a filesystem entry (will be initialized to default_parms each time a new entry is parsed in the build file)
  1810.  
  1811.    // bootable IFS support
  1812.    char *bootfile_pathname = NULL;           // HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS
  1813.    size_t bootfile_size = 0;                 // HACK: size of the bootcode binary blob file to put at the start of a bootable IFS
  1814.    char *startupfile_pathname = NULL;        // HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS
  1815.    size_t startupfile_ep_from_imagebase = 0; // HACK: startup code entrypoint offset from image base for a bootable IFS
  1816.    char *kernelfile_pathname = NULL;         // HACK: pathname to precompiled kernel file blob to put in a bootable IFS
  1817.    size_t kernelfile_offset = 0;             // HACK: kernel file offset in bootable IFS
  1818.  
  1819.    char path_on_buildhost[MAXPATHLEN] = "";
  1820.    char path_in_ifs[MAXPATHLEN] = "";
  1821.    char *ifs_pathname = NULL;
  1822.    void *reallocated_ptr;
  1823.    const elf_header_t *elf;
  1824.    elf_program_header_t *phdr;
  1825.    struct tm utc_time;
  1826.    struct stat stat_buf;
  1827.    size_t startuptrailer_offset;
  1828.    size_t startupheader_offset;
  1829.    size_t imagetrailer_offset;
  1830.    size_t imageheader_offset;
  1831.    size_t imgdir_offset;
  1832.    size_t imgdir_size;
  1833.    size_t final_size;
  1834.    size_t blob_size;
  1835.    size_t available_space;
  1836.    size_t allocated_size;
  1837.    size_t fsentry_index;
  1838.    size_t largest_index;
  1839.    size_t largest_size;
  1840.    size_t curr_offset;
  1841.    size_t table_index;
  1842.    size_t table_count;
  1843.    uint8_t *blob_data;
  1844.    int32_t checksum;
  1845.    char *specifiedpathname_start;
  1846.    char *directiveblock_start;
  1847.    char *write_ptr;
  1848.    char *line_ptr;
  1849.    char *token;
  1850.    char *value;
  1851.    char *sep;
  1852.    //char *ctx;
  1853.    int arg_index;
  1854.    bool is_quoted_context = false;
  1855.    bool is_escaped_char = false;
  1856.    bool has_data_already = false;
  1857.    bool want_info = false;
  1858.    bool want_everything = false;
  1859.    bool want_help = false;
  1860.    bool is_foreign_endianness;
  1861.    int string_len;
  1862.    int read_char;
  1863.    FILE *buildfile_fp;
  1864.    FILE *fp;
  1865.  
  1866.    // parse arguments
  1867.    for (arg_index = 1; arg_index < argc; arg_index++)
  1868.    {
  1869.       if ((strcmp (argv[arg_index], "--bootfile") == 0) && (arg_index + 1 < argc)) // --bootfile path/to/blob.bin
  1870.          bootfile_pathname = argv[++arg_index];
  1871.       else if ((strcmp (argv[arg_index], "--startupfile") == 0) && (arg_index + 1 < argc)) // --startupfile path/to/blob.bin@0x1030
  1872.       {
  1873.          sep = strchr (argv[++arg_index], '@');
  1874.          if ((sep == NULL) || (sep[1] == 0))
  1875.          {
  1876.             fprintf (stderr, "error: the --startupfile arguments expects <pathname>@<entrypoint_from_image_base>\n");
  1877.             exit (1);
  1878.          }
  1879.          *sep = 0;
  1880.          startupfile_pathname = argv[arg_index];
  1881.          startupfile_ep_from_imagebase = (size_t) read_integer (sep + 1);
  1882.       }
  1883.       else if ((strcmp (argv[arg_index], "--kernelfile") == 0) && (arg_index + 1 < argc)) // --kernelfile path/to/blob.bin@0x32000
  1884.       {
  1885.          sep = strchr (argv[++arg_index], '@');
  1886.          if ((sep == NULL) || (sep[1] == 0))
  1887.          {
  1888.             fprintf (stderr, "error: the --kernelfile arguments expects <pathname>@<fileoffset>\n");
  1889.             exit (1);
  1890.          }
  1891.          *sep = 0;
  1892.          kernelfile_pathname = argv[arg_index];
  1893.          kernelfile_offset = (size_t) read_integer (sep + 1);
  1894.       }
  1895.       else if (strcmp (argv[arg_index], "-n") == 0)
  1896.          default_parms.mtime_for_inline_files = 0; // inline files should have a mtime set to zero
  1897.       else if (strcmp (argv[arg_index], "-nn") == 0)
  1898.       {
  1899.          default_parms.mtime = 0; // all files should have a mtime set to zero
  1900.          default_parms.mtime_for_inline_files = 0;
  1901.       }
  1902.       else if (strcmp (argv[arg_index], "--info") == 0)
  1903.          want_info = true;
  1904.       else if (strcmp (argv[arg_index], "--everything") == 0)
  1905.          want_everything = true;
  1906.       else if ((strcmp (argv[arg_index], "-?") == 0) || (strcmp (argv[arg_index], "--help") == 0))
  1907.          want_help = true;
  1908.       else if (buildfile_pathname == NULL)
  1909.          buildfile_pathname = argv[arg_index];
  1910.       else if (ifs_pathname == NULL)
  1911.          ifs_pathname = argv[arg_index];
  1912.    }
  1913.  
  1914.    // do we not have enough information to run ?
  1915.    if (want_help || (buildfile_pathname == NULL) || (!want_info && (ifs_pathname == NULL)))
  1916.    {
  1917.       fprintf ((want_help ? stdout : stderr), "ifstool - QNX in-kernel filesystem creation utility by Pierre-Marie Baty <pm@pmbaty.com>\n");
  1918.       fprintf ((want_help ? stdout : stderr), "          version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
  1919.       if (!want_help)
  1920.          fprintf (stderr, "error: missing parameters\n");
  1921.       fprintf ((want_help ? stdout : stderr), "usage:\n");
  1922.       fprintf ((want_help ? stdout : stderr), "    ifstool [--bootfile <pathname>] [--startupfile <pathname>@<EP_from_imgbase>] [--kernelfile <pathname>@<fileoffs>] [-n[n]] <buildfile> <outfile>\n");
  1923.       fprintf ((want_help ? stdout : stderr), "    ifstool --info [--everything] <ifs file>\n");
  1924.       fprintf ((want_help ? stdout : stderr), "    ifstool --help\n");
  1925.       fprintf ((want_help ? stdout : stderr), "WARNING: the compilation feature is currently a work in progress (broken). Only the --info mode is usable at the moment.\n");
  1926.       exit (want_help ? 0 : 1);
  1927.    }
  1928.  
  1929.    // do we want info about a particular IFS ? if so, dump it
  1930.    if (want_info)
  1931.       exit (dump_ifs_info (buildfile_pathname, want_everything)); // NOTE: the first argument after --info is actually the IFS file, not a build file, but the arguments are collected in this order
  1932.  
  1933.    // make sure we have ${QNX_TARGET} pointing somewhere
  1934.    QNX_TARGET = getenv ("QNX_TARGET");
  1935.    if (QNX_TARGET == NULL)
  1936.    {
  1937.       fprintf (stderr, "error: the QNX_TARGET environment variable is not set\n");
  1938.       exit (1);
  1939.    }
  1940.    else if (access (QNX_TARGET, 0) != 0)
  1941.    {
  1942.       fprintf (stderr, "error: the QNX_TARGET environment variable doesn't point to an existing directory\n");
  1943.       exit (1);
  1944.    }
  1945.  
  1946.    // prepare a default MKIFS_PATH assuming the host processor
  1947.    update_MKIFS_PATH (image_processor);
  1948.  
  1949.    // open build file
  1950.    buildfile_fp = fopen (buildfile_pathname, "rb");
  1951.    if (buildfile_fp == NULL)
  1952.    {
  1953.       fprintf (stderr, "error: unable to open build file \"%s\" for reading (%s)\n", buildfile_pathname, strerror (errno));
  1954.       exit (1);
  1955.    }
  1956.  
  1957.    // stack up filesystem entries
  1958.    memcpy (&entry_parms, &default_parms, sizeof (default_parms));
  1959.    entry_parms.st_mode = S_IFDIR | default_parms.dperms;
  1960.    add_fsentry (&fsentries, &fsentry_count, &entry_parms, "", NULL); // add the root dir first
  1961.  
  1962.    while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL)
  1963.    {
  1964.       if (current_line != NULL)
  1965.          free (current_line);
  1966.       current_line = strdup (line_buffer);
  1967.       WELLMANNERED_ASSERT (current_line, "out of memory");
  1968.       lineno++; // keep track of current line number
  1969.       //fprintf (stderr, "read buildfile line %d: {%s}\n", lineno, line_buffer);
  1970.  
  1971.       line_ptr = line_buffer;
  1972.       while ((*line_ptr != 0) && isspace (*line_ptr))
  1973.          line_ptr++; // skip leading spaces
  1974.  
  1975.       if ((*line_ptr == 0) || (*line_ptr == '#'))
  1976.          continue; // skip empty or comment lines
  1977.  
  1978.       string_len = (int) strlen (line_buffer);
  1979.       if ((string_len > 0) && (line_buffer[string_len - 1] == '\n'))
  1980.          line_buffer[string_len - 1] = 0; // chop off newline for easier debug output
  1981.  
  1982.       // reset entry values
  1983.       memcpy (&entry_parms, &default_parms, sizeof (default_parms));
  1984.       path_in_ifs[0] = 0;
  1985.       path_on_buildhost[0] = 0;
  1986.       has_data_already = false;
  1987.  
  1988.       //fprintf (stderr, "parsing buildfile line %d: [%s]\n", lineno, line_ptr);
  1989.  
  1990.       // does this line start with an attribute block ?
  1991.       if (*line_ptr == '[')
  1992.       {
  1993.          line_ptr++; // skip the leading square bracket
  1994.          directiveblock_start = line_ptr; // remember where it starts
  1995.          is_quoted_context = false;
  1996.          while ((*line_ptr != 0) && !((*line_ptr == ']') && (line_ptr[-1] != '\\')))
  1997.          {
  1998.             if (*line_ptr == '"')
  1999.                is_quoted_context ^= true; // remember when we're between quotes
  2000.             else if (!is_quoted_context && (*line_ptr == ' '))
  2001.                *line_ptr = RECORD_SEP; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
  2002.             line_ptr++; // reach the next unescaped closing square bracket
  2003.          }
  2004.          if (*line_ptr != ']')
  2005.          {
  2006.             fprintf (stderr, "warning: syntax error in \"%s\" line %d: unterminated attributes block (skipping)\n", buildfile_pathname, lineno);
  2007.             continue; // invalid attribute block, skip line
  2008.          }
  2009.          *line_ptr = 0; // end the attribute block so that it is a parsable C string
  2010.  
  2011.          // now parse the attribute tokens
  2012.          // DOCUMENTATION: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/m/mkifs.html#mkifs__description
  2013.          token = strtok (directiveblock_start, RECORD_SEP_STR);
  2014.          while (token != NULL)
  2015.          {
  2016.             // evaluate attribute token
  2017.             #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
  2018.             if      (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.uid     = (int) read_integer (value); }
  2019.             else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.gid     = (int) read_integer (value); }
  2020.             else if (strncmp (token, "dperms=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms.dperms  = (int) read_integer (value); }
  2021.             else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms.perms   = (int) read_integer (value); }
  2022.             else if (strncmp (token, "type=",    5) == 0) { REACH_TOKEN_VALUE (); entry_parms.st_mode = (strcmp (value, "dir") == 0 ? S_IFDIR : (strcmp (value, "file") == 0 ? S_IFREG : (strcmp (value, "link") == 0 ? S_IFLNK : (strcmp (value, "fifo") == 0 ? S_IFIFO : (fprintf (stderr, "warning: invalid 'type' attribute in \"%s\" line %d: '%s', defaulting to 'file'\n", buildfile_pathname, lineno, value), S_IFREG))))); }
  2023.             else if (strncmp (token, "prefix=",  7) == 0) { REACH_TOKEN_VALUE (); strcpy (entry_parms.prefix, (*value == '/' ? value + 1 : value)); } // skip possible leading slash in prefix
  2024.             else if (strncmp (token, "image=",   6) == 0) { REACH_TOKEN_VALUE ();
  2025.                image_base = (uint32_t) read_integer (value); // read image base address
  2026.                if ((sep = strchr (value, '-')) != NULL) image_end       = (uint32_t) read_integer (sep + 1); // if we have a dash, read optional image end (FIXME: check this value and produce an error in the relevant case. Not important.)
  2027.                if ((sep = strchr (value, ',')) != NULL) image_maxsize   = (uint32_t) read_integer (sep + 1); // if we have a comma, read optional image max size
  2028.                if ((sep = strchr (value, '=')) != NULL) image_totalsize = (uint32_t) read_integer (sep + 1); // if we have an equal sign, read optional image padding size
  2029.                if ((sep = strchr (value, '%')) != NULL) image_align     = (uint32_t) read_integer (sep + 1); // if we have a modulo sign, read optional image aligmnent
  2030.                fprintf (stderr, "info: image 0x%x-0x%x maxsize %d totalsize %d align %d\n", image_base, image_end, image_maxsize, image_totalsize, image_align);
  2031.             }
  2032.             else if (strncmp (token, "virtual=", 8) == 0) { REACH_TOKEN_VALUE ();
  2033.                if ((bootfile_pathname == NULL) || (startupfile_pathname == NULL) || (kernelfile_pathname == NULL)) // HACK until I figure out how to re-create them
  2034.                {
  2035.                   fprintf (stderr, "error: creating bootable images require the --bootfile, --startupfile and --kernelfile command-line options in \"%s\" line %d\n", buildfile_pathname, lineno);
  2036.                   exit (1);
  2037.                }
  2038.                if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ?
  2039.                {
  2040.                   *sep = 0;
  2041.                   strcpy (image_processor, value); // save processor
  2042.                   update_MKIFS_PATH (image_processor);
  2043.                   value = sep + 1;
  2044.                }
  2045.                //sprintf (image_bootfile, "%s/%s/boot/sys/%s.boot", QNX_TARGET, image_processor, value); // save preboot file name (FIXME: we should search in MKIFS_PATH instead of this. Not important.)
  2046.                //strcpy (image_bootfile, bootfile_pathname); // FIXME: HACK
  2047.                if (stat (bootfile_pathname, &stat_buf) != 0)
  2048.                {
  2049.                   fprintf (stderr, "error: unable to stat the boot file \"%s\" specified in \"%s\" line %d: %s\n", bootfile_pathname, buildfile_pathname, lineno, strerror (errno));
  2050.                   exit (1);
  2051.                }
  2052.                bootfile_size = stat_buf.st_size; // save preboot file size
  2053.                fprintf (stderr, "info: processor \"%s\" bootfile \"%s\"\n", image_processor, bootfile_pathname);
  2054.                if (read_filecontents (kernelfile_pathname, ".", &entry_parms.data, &entry_parms.datalen) == NULL)
  2055.                {
  2056.                   fprintf (stderr, "fatal error: unable to read precompiled kernel file \"%s\" specified in --kernelfile argument\n", kernelfile_pathname);
  2057.                   exit (1);
  2058.                }
  2059.                has_data_already = true; // remember we already have data
  2060.             }
  2061.             else if (strncmp (token, "mtime=", 6) == 0) { REACH_TOKEN_VALUE (); if (strcmp (value, "*") == 0) entry_parms.mtime = UINT32_MAX; else {
  2062.                   // value *must* be "YYYY-MM-DD-HH:MM:SS" by specification
  2063.                   memset (&utc_time, 0, sizeof (utc_time));
  2064.                   if (sscanf (value, "%u-%u-%u-%u:%u:%u", &utc_time.tm_year, &utc_time.tm_mon, &utc_time.tm_mday, &utc_time.tm_hour, &utc_time.tm_min, &utc_time.tm_sec) != 6)
  2065.                   {
  2066.                      fprintf (stderr, "warning: syntax error in \"%s\" line %d: mtime specification not in YYYY-MM-DD-HH:MM:SS format (skipping)\n", buildfile_pathname, lineno);
  2067.                      continue; // invalid attribute block, skip line
  2068.                   }
  2069.                   utc_time.tm_mon--; // convert month from [1-12] to [0-11]
  2070.                   entry_parms.mtime = (uint32_t) mktime (&utc_time);
  2071.                }
  2072.             }
  2073.             else if (strcmp (token, "+script")     == 0) {
  2074.                entry_parms.is_compiled_bootscript = true;
  2075.                entry_parms.data = malloc (sizeof (INITIAL_STARTUP_SCRIPT) - 1);
  2076.                WELLMANNERED_ASSERT (entry_parms.data, "out of memory");
  2077.                memcpy (entry_parms.data, INITIAL_STARTUP_SCRIPT, sizeof (INITIAL_STARTUP_SCRIPT) - 1); // FIXME: HACK until the script compiler is implemented
  2078.                entry_parms.datalen = sizeof (INITIAL_STARTUP_SCRIPT) - 1;
  2079.                has_data_already = true; // remember we already have data
  2080.             }
  2081.             else if (strcmp (token, "-script")     == 0) entry_parms.is_compiled_bootscript = false;
  2082.             else if (strcmp (token, "+followlink") == 0) entry_parms.should_follow_symlinks = true;
  2083.             else if (strcmp (token, "-followlink") == 0) entry_parms.should_follow_symlinks = false;
  2084.             else if (strcmp (token, "+autolink")   == 0) entry_parms.should_autosymlink_dylib = true;
  2085.             else if (strcmp (token, "-autolink")   == 0) entry_parms.should_autosymlink_dylib = false;
  2086.             else fprintf (stderr, "warning: unimplemented attribute in \"%s\" line %d: '%s'\n", buildfile_pathname, lineno, token);
  2087.             #undef REACH_TOKEN_VALUE
  2088.  
  2089.             token = strtok (NULL, RECORD_SEP_STR); // proceed to next attribute token
  2090.          }
  2091.  
  2092.          line_ptr++; // reach the next character
  2093.          while ((*line_ptr != 0) && isspace (*line_ptr))
  2094.             line_ptr++; // skip leading spaces
  2095.  
  2096.          // are we at the end of the line ? if so, it means the attribute values that are set should become the default
  2097.          if ((*line_ptr == 0) || (*line_ptr == '#'))
  2098.          {
  2099.             #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (entry_parms.attr != default_parms.attr) { \
  2100.                   fprintf (stderr, "info: changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d\n", default_parms.attr, entry_parms.attr, buildfile_pathname, lineno); \
  2101.                   default_parms.attr = entry_parms.attr; \
  2102.                } } while (0)
  2103.             #define APPLY_DEFAULT_ATTR_STR(attr,descr,fmt) do { if (strcmp (entry_parms.attr, default_parms.attr) != 0) { \
  2104.                   fprintf (stderr, "info: changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d\n", default_parms.attr, entry_parms.attr, buildfile_pathname, lineno); \
  2105.                   strcpy (default_parms.attr, entry_parms.attr); \
  2106.                } } while (0)
  2107.             APPLY_DEFAULT_ATTR_NUM (dperms,                   "directory permissions",           "0%o");
  2108.             APPLY_DEFAULT_ATTR_NUM (perms,                    "file permissions",                "0%o");
  2109.             APPLY_DEFAULT_ATTR_NUM (uid,                      "owner ID",                        "%d");
  2110.             APPLY_DEFAULT_ATTR_NUM (gid,                      "group ID",                        "%d");
  2111.             APPLY_DEFAULT_ATTR_NUM (st_mode,                  "inode type",                      "0%o");
  2112.             APPLY_DEFAULT_ATTR_STR (prefix,                   "prefix",                          "\"%s\"");
  2113.             APPLY_DEFAULT_ATTR_NUM (is_compiled_bootscript,   "compiled script state",           "%d");
  2114.             APPLY_DEFAULT_ATTR_NUM (should_follow_symlinks,   "symlink resolution",              "%d");
  2115.             APPLY_DEFAULT_ATTR_NUM (should_autosymlink_dylib, "dylib canonical name symlinking", "%d");
  2116.             #undef APPLY_DEFAULT_ATTR_STR
  2117.             #undef APPLY_DEFAULT_ATTR_NUM
  2118.             continue; // end of line reached, proceed to the next line
  2119.          }
  2120.          // end of attributes parsing
  2121.       } // end of "this line starts with an attributes block"
  2122.  
  2123.       // there's data in this line. We expect a filename in the IFS. Read it and unescape escaped characters
  2124.       string_len = sprintf (path_in_ifs, "%s", entry_parms.prefix);
  2125.       while ((string_len > 0) && (path_in_ifs[string_len - 1] == '/'))
  2126.          string_len--; // chop off any trailing slashes from prefix
  2127.       write_ptr = &path_in_ifs[string_len];
  2128.       *write_ptr++ = '/'; // add ONE trailing slash
  2129.       specifiedpathname_start = write_ptr; // remember the specified pathname will start here
  2130.       is_quoted_context = (*line_ptr == '"');
  2131.       if (is_quoted_context)
  2132.          line_ptr++; // skip a possible initial quote
  2133.       if (*line_ptr == '/')
  2134.       {
  2135.          fprintf (stderr, "warning: paths in the IFS file should not begin with a leading '/' in \"%s\" line %d\n", buildfile_pathname, lineno);
  2136.          line_ptr++; // consistency check: paths in the IFS should not begin with a '/'
  2137.       }
  2138.       while ((*line_ptr != 0) && ((!is_quoted_context && (*line_ptr != '=') && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"'))))
  2139.       {
  2140.          if (*line_ptr == '\\')
  2141.          {
  2142.             line_ptr++;
  2143.             *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
  2144.          }
  2145.          else
  2146.             *write_ptr++ = *line_ptr;
  2147.          line_ptr++;
  2148.       }
  2149.       *write_ptr = 0; // terminate the string
  2150.       if (is_quoted_context && (*line_ptr == '"'))
  2151.          line_ptr++; // skip a possible final quote
  2152.  
  2153.       // we reached a space OR an equal sign
  2154.       while ((*line_ptr != 0) && isspace (*line_ptr))
  2155.          line_ptr++; // skip optional spaces after the filename in the IFS
  2156.  
  2157.       // do we have an equal sign ?
  2158.       if (*line_ptr == '=') // we must be creating either a directory or a file, do we have an equal sign ?
  2159.       {
  2160.          line_ptr++; // skip the equal sign
  2161.          while ((*line_ptr != 0) && isspace (*line_ptr))
  2162.             line_ptr++; // skip optional spaces after the equal sign
  2163.  
  2164.          if (*line_ptr == 0)
  2165.          {
  2166.             fprintf (stderr, "warning: syntax error in \"%s\" line %d: missing data specification after equal sign (skipping)\n", buildfile_pathname, lineno);
  2167.             continue; // invalid symlink specification, skip line
  2168.          }
  2169.  
  2170.          // read the host system's path, it may be either a path or a contents definition. Is it a content definition ?
  2171.          if (*line_ptr == '{')
  2172.          {
  2173.             path_on_buildhost[0] = 0; // this is an inline fine, which means it doesn't exist on the build host
  2174.             allocated_size = 0;
  2175.  
  2176.             line_ptr++; // skip the leading content definition
  2177.             is_escaped_char = false;
  2178.             for (;;)
  2179.             {
  2180.                read_char = fgetc (buildfile_fp);
  2181.                if (read_char == EOF)
  2182.                {
  2183.                   fprintf (stderr, "fatal error: syntax error in \"%s\" line %d: unterminated contents block (end of file reached)\n", buildfile_pathname, lineno);
  2184.                   exit (1); // invalid contents block
  2185.                }
  2186.                else if ((read_char == '\\') && !is_escaped_char)
  2187.                   is_escaped_char = true; // remember the next char is escaped
  2188.                else if ((read_char == '}') && !is_escaped_char)
  2189.                   break; // found an unescaped closing bracked, stop parsing
  2190.                else
  2191.                {
  2192.                   is_escaped_char = false; // any other char, meaning the next one will not be escaped
  2193.                   if (!has_data_already) // only store the contents if we do NOT know the data yet
  2194.                   {
  2195.                      if (entry_parms.datalen == allocated_size) // reallocate in 4 kb blocks
  2196.                      {
  2197.                         reallocated_ptr = realloc (entry_parms.data, allocated_size + 4096);
  2198.                         WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
  2199.                         entry_parms.data = reallocated_ptr;
  2200.                         allocated_size += 4096;
  2201.                      }
  2202.                      entry_parms.data[entry_parms.datalen++] = read_char;
  2203.                   }
  2204.                   if (read_char == '\n')
  2205.                      lineno++; // update line counter as we parse the inline content
  2206.                }
  2207.             } // end for
  2208.             has_data_already = true; // remember we have data now
  2209.          }
  2210.          else // not a content definition between { brackets }, must be either a pathname on the build host, or the target of a symlink
  2211.          {
  2212.             is_quoted_context = (*line_ptr == '"');
  2213.             if (is_quoted_context)
  2214.                line_ptr++; // skip a possible initial quote
  2215.             specifiedpathname_start = line_ptr; // remember where the specified pathname starts
  2216.             write_ptr = line_ptr; // now unescape all characters
  2217.             while ((*line_ptr != 0) && ((!is_quoted_context && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"'))))
  2218.             {
  2219.                if (*line_ptr == '\\')
  2220.                {
  2221.                   line_ptr++;
  2222.                   *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
  2223.                }
  2224.                else
  2225.                   *write_ptr++ = *line_ptr;
  2226.                line_ptr++;
  2227.             }
  2228.             *write_ptr = 0; // terminate the string
  2229.             if (is_quoted_context && (*line_ptr == '"'))
  2230.                line_ptr++; // skip a possible final quote
  2231.  
  2232.             if (S_ISLNK (entry_parms.st_mode)) // are we storing a symlink ?
  2233.             {
  2234.                entry_parms.data = strdup (specifiedpathname_start); // if so, store the symlink target as the dirent's blob data
  2235.                WELLMANNERED_ASSERT (entry_parms.data, "out of memory");
  2236.                entry_parms.datalen = strlen (specifiedpathname_start);
  2237.                has_data_already = true; // remember we have data now
  2238.             }
  2239.             else // it's a build host filesystem path
  2240.                strcpy (path_on_buildhost, line_ptr); // the path on the build host is given after the equal sign
  2241.          }
  2242.       }
  2243.       else // no equal sign, meaning the file will have the same name on the build host filesystem
  2244.       {
  2245.          // consistency check: symlinks MUST have an equal sign
  2246.          if (entry_parms.st_mode == S_IFLNK)
  2247.          {
  2248.             fprintf (stderr, "warning: syntax error in \"%s\" line %d: missing equal sign and symlink target (skipping)\n", buildfile_pathname, lineno);
  2249.             continue; // invalid symlink specification, skip line
  2250.          }
  2251.  
  2252.          strcpy (path_on_buildhost, specifiedpathname_start); // the path on the build host is the one specified
  2253.          sep = strrchr (specifiedpathname_start, '/');
  2254.          if (sep != NULL)
  2255.             memmove (specifiedpathname_start, sep + 1, strlen (sep + 1) + 1); // the path in the IFS will be the BASENAME of the path specified (after the prefix)
  2256.       }
  2257.  
  2258.       // now add this entry to the image filesystem
  2259.       if (S_ISDIR (entry_parms.st_mode))
  2260.          entry_parms.st_mode |= entry_parms.dperms;
  2261.       else if (S_ISLNK (entry_parms.st_mode))
  2262.          entry_parms.st_mode |= 0777; // NOTE: mkifs sets symlink permissions to rwxrwxrwx !?
  2263.       else // file or device node
  2264.          entry_parms.st_mode |= entry_parms.perms;
  2265.  
  2266.       add_fsentry (&fsentries, &fsentry_count, &entry_parms, path_in_ifs, path_on_buildhost); // and add filesystem entry
  2267.  
  2268.       if (entry_parms.data != NULL)
  2269.          free (entry_parms.data); // if blob data was allocated, free it
  2270.    }
  2271.  
  2272.    // write IFS file
  2273.    fp = fopen (ifs_pathname, "w+b");
  2274.    if (fp == NULL)
  2275.    {
  2276.       fprintf (stderr, "error: failed to open \"%s\" for writing (%s)\n", ifs_pathname, strerror (errno));
  2277.       exit (1);
  2278.    }
  2279.  
  2280.    // do we have a startup file ? if so, this is a bootable image
  2281.    if (startupfile_pathname != NULL)
  2282.    {
  2283.       // write boot prefix
  2284.       fwrite_filecontents (bootfile_pathname, fp);
  2285.       PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  2286.  
  2287.       startupheader_offset = ftell (fp); // save startup header offset
  2288.       memset (&startup_header, 0, sizeof (startup_header)); // prepare startup header
  2289.       memcpy (startup_header.signature, "\xeb\x7e\xff\x00", 4); // startup header signature, i.e. 0xff7eeb
  2290.       startup_header.version       = 1;
  2291.       startup_header.flags1        = STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2; // flags, 0x21 (STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2)
  2292.       startup_header.header_size   = sizeof (startup_header); // 256
  2293.       if (strcmp (image_processor, "x86_64") == 0)
  2294.          startup_header.machine = ELF_MACHINE_X86_64; // EM_X86_64
  2295.       else if (strcmp (image_processor, "aarch64le") == 0)
  2296.          startup_header.machine = ELF_MACHINE_AARCH64; // EM_AARCH64
  2297.       else
  2298.       {
  2299.          fprintf (stderr, "fatal error: unsupported processor type '%s' found in build file \"%s\"\n", image_processor, buildfile_pathname);
  2300.          exit (1);
  2301.       }
  2302.       startup_header.startup_vaddr = image_base + (uint32_t) startupfile_ep_from_imagebase; // [I ] Virtual Address to transfer to after IPL is done, here 0x01403008 (appears in "Entry" column for "startup.*")
  2303.       startup_header.image_paddr   = image_base + (uint32_t) bootfile_size;                 // F[IS] Physical address of image, here 0x01400f30 (appears in "Offset" column for "startup-header" which is the first entry/start of file)
  2304.       startup_header.ram_paddr     = startup_header.image_paddr;                            // [IS] Physical address of RAM to copy image to (startup_size bytes copied), here 0x01400f30 (same as above)
  2305.       startup_header.ram_size      = WILL_BE_FILLED_LATER;                                  // [ S] Amount of RAM used by the startup program and executables contained in the file system, here 0x00cd6128 i.e. 13 459 752 dec. which is 13 Mb. i.e. IFS file size minus 0x9eee (40686)
  2306.       startup_header.startup_size  = WILL_BE_FILLED_LATER;                                  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
  2307.       startup_header.stored_size   = WILL_BE_FILLED_LATER;                                  // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
  2308.       startup_header.imagefs_size  = WILL_BE_FILLED_LATER;                                  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
  2309.       startup_header.preboot_size  = (uint16_t) bootfile_size;                              // [I ] Size of loaded before header, here 0xf30 or 3888 bytes (size of "bios.boot" file))
  2310.       fwrite_or_die (&startup_header, 1, sizeof (startup_header), fp); // write startup header
  2311.       PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  2312.  
  2313.       // ######################################################################################################################################################################################################################################
  2314.       // # FIXME: figure out how to re-create it: linker call involved
  2315.       // # $ x86_64-pc-nto-qnx8.0.0-ld --sysroot=${QNX_TARGET}/x86_64/ -T${QNX_TARGET}/x86_64/lib/nto.link --section-start .text=0x1401030 --no-relax ${QNX_TARGET}/x86_64/boot/sys/startup-x86 -o startup.bin.UNSTRIPPED
  2316.       // ######################################################################################################################################################################################################################################
  2317.       fwrite_filecontents (startupfile_pathname, fp); // write startup code from blob file
  2318.       PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  2319.  
  2320.       startuptrailer_offset = ftell (fp); // save startup trailer offset
  2321.       fwrite_or_die (&startup_trailer, 1, sizeof (startup_trailer), fp); // write startup trailer
  2322.       PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  2323.    }
  2324.  
  2325.    imageheader_offset = ftell (fp); // save image header offset
  2326.    memset (&image_header, 0, sizeof (image_header)); // prepare image header
  2327.    memcpy (&image_header.signature, "imagefs", 7); // image filesystem signature, i.e. "imagefs"
  2328.    image_header.flags         = IMAGE_FLAGS_TRAILER_V2 | IMAGE_FLAGS_SORTED | IMAGE_FLAGS_INO_BITS; // endian neutral flags, 0x1c (IMAGE_FLAGS_TRAILER_V2 | IMAGE_FLAGS_SORTED | IMAGE_FLAGS_INO_BITS)
  2329.    image_header.image_size    = WILL_BE_FILLED_LATER; // size from header to end of trailer (here 0xca6fe0 or 13 266 912)
  2330.    image_header.hdr_dir_size  = WILL_BE_FILLED_LATER; // size from header to last dirent (here 0x12b8 or 4792)
  2331.    image_header.dir_offset    = sizeof (image_header); // offset from header to first dirent (here 0x5c or 92)
  2332.    image_header.boot_ino[0]   = image_kernel_ino; // inode of files for bootstrap p[ro?]g[ra?]ms (here 0xa0000002, 0, 0, 0)
  2333.    image_header.script_ino    = image_bootscript_ino; // inode of file for script (here 3)
  2334.    image_header.mountpoint[0] = '/'; // default mountpoint for image ("/" + "\0\0\0")
  2335.    fwrite_or_die (&image_header, 1, sizeof (image_header), fp); // write image header
  2336.    PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  2337.  
  2338.    // write image directory (with the wrong file offsets)
  2339.    imgdir_offset = ftell (fp);
  2340.    imgdir_size = 0; // measure image dir size on the fly
  2341.    for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
  2342.       imgdir_size += fwrite_fsentry (&fsentries[fsentry_index], fp); // NOTE: padding is handled in this function
  2343.  
  2344.    fwrite_or_die ("\0\0\0\0", 1, 4, fp); // there seems to be 4 bytes of padding after the image directory
  2345.    imgdir_size += 4;
  2346.  
  2347.    // is it a bootable image with a kernel file ?
  2348.    if ((startupfile_pathname != NULL) && (kernelfile_pathname != NULL))
  2349.    {
  2350.       // start by writing the startup script data blob, if we have one
  2351.       for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
  2352.          if (fsentries[fsentry_index].header.ino == image_bootscript_ino)
  2353.             break; // locate the startup script directory entry
  2354.       if (fsentry_index < fsentry_count) // found it ?
  2355.       {
  2356.          curr_offset = ftell (fp);
  2357.          if (curr_offset + fsentries[fsentry_index].u.file.size >= kernelfile_offset)
  2358.          {
  2359.             fprintf (stderr, "error: the compiled startup script is too big (%zd bytes, max is %zd) to fit at current offset %zd\n", (size_t) fsentries[fsentry_index].u.file.size, kernelfile_offset - curr_offset, curr_offset);
  2360.             exit (1);
  2361.          }
  2362.          fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure
  2363.          fwrite_or_die (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
  2364.          fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
  2365.       }
  2366.  
  2367.       // now write the filesystem entries that may fit before the kernel
  2368.       for (;;)
  2369.       {
  2370.          curr_offset = ftell (fp); // see where we are
  2371.          available_space = kernelfile_offset - curr_offset; // measure the available space
  2372.  
  2373.          // look for the biggest one that can fit
  2374.          largest_index = 0;
  2375.          largest_size = 0;
  2376.          for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
  2377.          {
  2378.             if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written || (fsentries[fsentry_index].u.file.size > available_space))
  2379.                continue; // skip all entries that don't have a separate data block, those who were written already and those that wouldn't fit
  2380.             if (fsentries[fsentry_index].u.file.size > largest_size)
  2381.             {
  2382.                largest_size = fsentries[fsentry_index].u.file.size;
  2383.                largest_index = fsentry_index;
  2384.             }
  2385.          }
  2386.          if (largest_size == 0)
  2387.             break; // found none ? if so, stop searching
  2388.  
  2389.          // is the file we're storing a preprocessed ELF file ?
  2390.          if (fsentries[fsentry_index].header.ino & IFS_INO_PROCESSED_ELF)
  2391.          {
  2392.             elf = (elf_header_t *) fsentries[fsentry_index].u.file.UNSAVED_databuf; // quick access to ELF header
  2393.             table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers
  2394.             for (table_index = 0; table_index < table_count; table_index++)
  2395.             {
  2396.                phdr = (elf_program_header_t *) &fsentries[fsentry_index].u.file.UNSAVED_databuf[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header
  2397.                ELF_SET_NUMERIC (elf, phdr, physical_addr, ELF_GET_NUMERIC (elf, phdr, physical_addr) + (0x1400000 + curr_offset)); // patch the physical address member of the program header table
  2398.             }
  2399.          }
  2400.  
  2401.          fsentries[largest_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure
  2402.          fwrite_or_die (fsentries[largest_index].u.file.UNSAVED_databuf, 1, fsentries[largest_index].u.file.size, fp); // write file data blob
  2403.          fsentries[largest_index].UNSAVED_was_data_written = true; // and remember this file's data was written
  2404.       }
  2405.       fprintf (stderr, "Current offset: 0x%zx\n", curr_offset);
  2406.       fprintf (stderr, "Kernel file offset: 0x%zx\n", kernelfile_offset);
  2407.       PAD_OUTFILE_TO (kernelfile_offset); // reach the kernel offset
  2408.  
  2409.       // now write the QNX kernel
  2410.       for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
  2411.          if (fsentries[fsentry_index].header.ino == image_kernel_ino)
  2412.             break; // locate the kernel directory entry (can't fail)
  2413.       curr_offset = ftell (fp); // see where we are
  2414.       fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure
  2415.       // ######################################################################################################################################################################################################################################
  2416.       // # FIXME: figure out how to re-create it: linker call involved
  2417.       // # $ x86_64-pc-nto-qnx8.0.0-ld --sysroot=${QNX_TARGET}/x86_64/ -T${QNX_TARGET}/x86_64/lib/nto.link --section-start .text=0xffff800000001000 --no-relax ${QNX_TARGET}/x86_64/boot/sys/procnto-smp-instr -o procnto-smp-instr.sym.UNSTRIPPED
  2418.       // ######################################################################################################################################################################################################################################
  2419.       fwrite_filecontents (kernelfile_pathname, fp); // write kernel from blob file
  2420.       PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  2421.       fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
  2422.    }
  2423.  
  2424.    // then write all the other files by increasing inode number: ELF files first
  2425.    for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
  2426.    {
  2427.       if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written // filter out anything that's not a file, and anything that's been already written
  2428.           || (fsentries[fsentry_index].u.file.size < 4) || (memcmp (fsentries[fsentry_index].u.file.UNSAVED_databuf, ELF_MAGIC_STR, 4) != 0)) // filter out anything that's not an ELF file
  2429.          continue; // skip all entries that don't have a separate data block and those who were written already
  2430.       curr_offset = ftell (fp);
  2431.       fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure
  2432.  
  2433.       // is the file we're storing a preprocessed ELF file ?
  2434.       if (fsentries[fsentry_index].header.ino & IFS_INO_PROCESSED_ELF)
  2435.       {
  2436.          elf = (elf_header_t *) fsentries[fsentry_index].u.file.UNSAVED_databuf; // quick access to ELF header
  2437.          table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers
  2438.          for (table_index = 0; table_index < table_count; table_index++)
  2439.          {
  2440.             phdr = (elf_program_header_t *) &fsentries[fsentry_index].u.file.UNSAVED_databuf[ELF_GET_NUMERIC (elf, elf, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (elf, elf, program_header_item_size) * table_index]; // quick access to program header
  2441.             ELF_SET_NUMERIC (elf, phdr, physical_addr, ELF_GET_NUMERIC (elf, phdr, physical_addr) + (0x1400000 + curr_offset)); // patch the physical address member of the program header table
  2442.          }
  2443.       }
  2444.  
  2445.       fwrite_or_die (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
  2446.       fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
  2447.    }
  2448.    for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) // other files (non-ELF, e.g. scripts and data files) last
  2449.    {
  2450.       if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written) // filter out anything that's not a file, and anything that's been already written
  2451.          continue; // skip all entries that don't have a separate data block and those who were written already
  2452.       curr_offset = ftell (fp);
  2453.       fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure
  2454.       fwrite_or_die (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
  2455.       fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
  2456.    }
  2457.    PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  2458.  
  2459.    // finally, write trailer (including empty checksum)
  2460.    imagetrailer_offset = ftell (fp); // save image trailer offset
  2461.    fwrite_or_die (&image_trailer, 1, sizeof (image_trailer), fp); // write image trailer
  2462.    PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
  2463.  
  2464.    // if we need to pad it to a specific length, do so
  2465.    PAD_OUTFILE_TO (image_totalsize);
  2466.    final_size = ftell (fp);
  2467.  
  2468.    // see if we are past the image max size, in which case it's an error
  2469.    if (final_size > image_maxsize)
  2470.    {
  2471.       fprintf (stderr, "error: image file \"%s\" size %zd exceeds max size (%zd)\n", ifs_pathname, final_size, (size_t) image_maxsize);
  2472.       exit (1);
  2473.    }
  2474.  
  2475.    // do we have a startup file ? if so, this is a bootable image
  2476.    if (startupfile_pathname != NULL)
  2477.    {
  2478.       // rewrite startup header with final values
  2479.       fseek_or_die (fp, startupheader_offset, SEEK_SET);
  2480.       startup_header.startup_size = (uint32_t) (imageheader_offset - startupheader_offset); // size of startup header up to image header
  2481.       startup_header.imagefs_size = (uint32_t) (final_size - imageheader_offset); // size of uncompressed imagefs
  2482.       startup_header.ram_size = (uint32_t) final_size; // FIXME: this is necessarily less, but should we really bother calculating the right size ?
  2483.       startup_header.stored_size = (uint32_t) final_size;
  2484.       fwrite_or_die (&startup_header, 1, sizeof (startup_header), fp); // write startup header
  2485.    }
  2486.  
  2487.    // rewrite image header with final values
  2488.    fseek_or_die (fp, imageheader_offset, SEEK_SET);
  2489.    image_header.image_size = (uint32_t) (final_size - imageheader_offset); // size of uncompressed imagefs
  2490.    image_header.hdr_dir_size = sizeof (image_header) + (uint32_t) imgdir_size; // size from start of image header to last dirent
  2491.    fwrite_or_die (&image_header, 1, sizeof (image_header), fp); // write image header
  2492.  
  2493.    // rewrite image directory with final offset values
  2494.    fseek_or_die (fp, imgdir_offset, SEEK_SET);
  2495.    if (image_header.flags & IMAGE_FLAGS_SORTED)
  2496.       qsort (&fsentries[1], fsentry_count - 1, sizeof (fsentry_t), fsentry_compare_pathnames_cb); // sort the filesystem entries by pathname
  2497.    for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
  2498.       fwrite_fsentry (&fsentries[fsentry_index], fp);
  2499.  
  2500.    fclose (fp); // ensure everything is flushed
  2501.  
  2502.    // ALL CHECKSUMS AT THE VERY END
  2503.  
  2504.    blob_data = NULL;
  2505.    read_filecontents (ifs_pathname, ".", &blob_data, &blob_size);
  2506.    WELLMANNERED_ASSERT (blob_data, "failed to open IFS file for checksumming: %s", strerror (errno));
  2507.  
  2508.    // do we have a startup file ? if so, this is a bootable image
  2509.    if (startupfile_pathname != NULL)
  2510.    {
  2511.       // compute SHA-512 checksum and V1 checksum of startup block
  2512.       if (   ( (startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
  2513.           || (!(startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
  2514.          is_foreign_endianness = true; // if the header is big endian and we're on a little endian machine, or the other way around, it's a foreign endianness
  2515.       else
  2516.          is_foreign_endianness = false; // else this header is for the same endianness as us
  2517.  
  2518.       SHA512 (&blob_data[startupheader_offset], startuptrailer_offset - startupheader_offset, &blob_data[startuptrailer_offset]); // compute SHA512 checksum and write it in place in blob data
  2519.       checksum = update_checksum (&blob_data[startupheader_offset], startuptrailer_offset + SHA512_DIGEST_LENGTH - startupheader_offset, is_foreign_endianness); // compute old checksum
  2520.       memcpy (&blob_data[startuptrailer_offset + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
  2521.    }
  2522.  
  2523.    // compute SHA-512 checksum and V1 checksum of image block
  2524.    if (   ( (image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
  2525.        || (!(image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
  2526.       is_foreign_endianness = true; // if the header is big endian and we're on a little endian machine, or the other way around, it's a foreign endianness
  2527.    else
  2528.       is_foreign_endianness = false; // else this header is for the same endianness as us
  2529.  
  2530.    SHA512 (&blob_data[imageheader_offset], imagetrailer_offset - imageheader_offset, &blob_data[imagetrailer_offset]); // compute SHA512 checksum and write it in place in blob data
  2531.    checksum = update_checksum (&blob_data[imageheader_offset], imagetrailer_offset + SHA512_DIGEST_LENGTH - imageheader_offset, is_foreign_endianness); // compute old checksum
  2532.    memcpy (&blob_data[imagetrailer_offset + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
  2533.  
  2534.    // now rewrite IFS with the correct checksums
  2535.    fp = fopen (ifs_pathname, "wb");
  2536.    WELLMANNERED_ASSERT (fp, "failed to reopen IFS file for checksumming: %s", strerror (errno));
  2537.    fwrite_or_die (blob_data, 1, blob_size, fp);
  2538.    fclose (fp);
  2539.    free (blob_data);
  2540.  
  2541.    // finished, exit with a success code
  2542.    fprintf (stdout, "Success\n");
  2543.    exit (0);
  2544. }
  2545.  
  2546.  
  2547. static int dump_ifs_info (const char *ifs_pathname, bool want_everything)
  2548. {
  2549.    #define hex_printf(buf,size,...) do { \
  2550.       if (want_everything || ((size) <= 16 * 1024)) /* only print when it's not too big (up to 16 kb) */\
  2551.          hex_fprintf (stdout, (buf), (size), 16, __VA_ARGS__); /* use 16 columns in hex output to stdout */ \
  2552.       else { \
  2553.          printf (__VA_ARGS__); \
  2554.          hex_fprintf (stdout, (buf), 1024, 16, "   first kilobyte:\n"); \
  2555.       } \
  2556.    } while (0)
  2557.    #define BINARY(x) binary ((x), '-', 'x')
  2558.  
  2559.    static const char *startupheader_flags1_strings[8] = {
  2560.       "VIRTUAL", // bit 0
  2561.       "BIGENDIAN", // bit 1
  2562.       "COMPRESS_BIT1", // bit 2
  2563.       "COMPRESS_BIT2", // bit 3
  2564.       "COMPRESS_BIT3", // bit 4
  2565.       "TRAILER_V2", // bit 5
  2566.       "", // bit 6
  2567.       "", // bit 7
  2568.    };
  2569.    static const char *imageheader_flags_strings[8] = {
  2570.       "BIGENDIAN", // bit 0
  2571.       "READONLY", // bit 1
  2572.       "INO_BITS", // bit 2
  2573.       "SORTED", // bit 3
  2574.       "TRAILER_V2", // bit 4
  2575.       "", // bit 5
  2576.       "", // bit 6
  2577.       "", // bit 7
  2578.    };
  2579.  
  2580.    startup_header_t *startup_header = NULL;
  2581.    size_t startupheader_offset = 0;
  2582.    startup_trailer_v1_t *startup_trailer_v1 = NULL;
  2583.    startup_trailer_v2_t *startup_trailer_v2 = NULL;
  2584.    size_t startuptrailer_offset = 0;
  2585.    image_header_t *image_header = NULL;
  2586.    size_t imageheader_offset = 0;
  2587.    image_trailer_v1_t *image_trailer_v1 = NULL;
  2588.    image_trailer_v2_t *image_trailer_v2 = NULL;
  2589.    size_t imagetrailer_offset = 0;
  2590.    fsentry_t **fsentries = NULL; // mallocated
  2591.    size_t fsentry_count = 0;
  2592.    fsentry_t *current_fsentry = NULL;
  2593.    char recorded_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
  2594.    char computed_sha512[2 * SHA512_DIGEST_LENGTH + 1] = "";
  2595.    size_t startupfile_blobsize = 0;
  2596.    void *reallocated_ptr;
  2597.    bool is_foreign_endianness;
  2598.    size_t bootfile_blobsize = 0;
  2599.    size_t current_offset;
  2600.    size_t fsentry_index;
  2601.    size_t nearest_distance;
  2602.    size_t nearest_index;
  2603.    size_t byte_index;
  2604.    uint32_t recorded_checksum;
  2605.    uint32_t computed_checksum;
  2606.    uint8_t *filedata;
  2607.    size_t filesize;
  2608.    time_t mtime;
  2609.  
  2610.    // open and read IFS file
  2611.    if (read_filecontents (ifs_pathname, ".", &filedata, &filesize) == NULL)
  2612.    {
  2613.       fprintf (stderr, "error: can't open \"%s\" for reading: %s\n", ifs_pathname, strerror (errno));
  2614.       return (1);
  2615.    }
  2616.  
  2617.    printf ("QNX In-kernel Filesystem analysis produced by ifstool version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
  2618.    printf ("IFS file \"%s\" - size 0x%zx (%zd) bytes\n", ifs_pathname, filesize, filesize);
  2619.  
  2620.    // parse file from start to end
  2621.    current_offset = 0;
  2622.    for (;;)
  2623.    {
  2624.       // does a startup header start here ?
  2625.       if ((current_offset + sizeof (startup_header_t) < filesize) && (memcmp (&filedata[current_offset], "\xeb\x7e\xff\x00", 4) == 0))
  2626.       {
  2627.          startupheader_offset = current_offset;
  2628.          startup_header = (startup_header_t *) &filedata[startupheader_offset];
  2629.  
  2630.          // layout:
  2631.          // [STARTUP HEADER]
  2632.          // (startup file blob)
  2633.          // [STARTUP TRAILER v1 or v2]
  2634.  
  2635.          printf ("\n");
  2636.          printf ("Startup header at offset 0x%zx (%zd):\n", current_offset, current_offset);
  2637.          printf ("   signature     = %02x %02x %02x %02x - good\n", startup_header->signature[0], startup_header->signature[1], startup_header->signature[2], startup_header->signature[3]);
  2638.          printf ("   version       = 0x%04x (%d) - %s\n", startup_header->version, startup_header->version, (startup_header->version == 1 ? "looks good" : "???"));
  2639.          printf ("   flags1        = 0x%02x (%s)\n", startup_header->flags1, describe_uint8 (startup_header->flags1, startupheader_flags1_strings));
  2640.          printf ("   flags2        = 0x%02x (%s) - %s\n", startup_header->flags2, BINARY (startup_header->flags2), (startup_header->flags2 == 0 ? "looks good" : "???"));
  2641.          printf ("   header_size   = 0x%04x (%d) - %s\n", startup_header->header_size, startup_header->header_size, (startup_header->header_size == sizeof (startup_header_t) ? "looks good" : "BAD"));
  2642.          printf ("   machine       = 0x%04x (%d) - %s\n", startup_header->machine, startup_header->machine, (startup_header->machine == ELF_MACHINE_X86_64 ? "x86_64" : (startup_header->machine == ELF_MACHINE_AARCH64 ? "aarch64" : "unknown")));
  2643.          printf ("   startup_vaddr = 0x%08x (%d) - virtual address to transfer to after IPL is done\n", startup_header->startup_vaddr, startup_header->startup_vaddr);
  2644.          printf ("   paddr_bias    = 0x%08x (%d) - value to add to physical addresses to get an indirectable pointer value\n", startup_header->paddr_bias, startup_header->paddr_bias);
  2645.          printf ("   image_paddr   = 0x%08x (%d) - physical address of image\n", startup_header->image_paddr, startup_header->image_paddr);
  2646.          printf ("   ram_paddr     = 0x%08x (%d) - physical address of RAM to copy image to (startup_size bytes copied)\n", startup_header->ram_paddr, startup_header->ram_paddr);
  2647.          printf ("   ram_size      = 0x%08x (%d) - amount of RAM used by the startup program and executables in the fs\n", startup_header->ram_size, startup_header->ram_size);
  2648.          printf ("   startup_size  = 0x%08x (%d) - size of startup (never compressed) - %s\n", startup_header->startup_size, startup_header->startup_size, (current_offset + sizeof (image_header_t) + startup_header->startup_size + (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)) < filesize ? "looks good" : "BAD (IFS file too short)"));
  2649.          printf ("   stored_size   = 0x%08x (%d) - size of entire image - %s\n", startup_header->stored_size, startup_header->stored_size, (startup_header->stored_size == startup_header->ram_size ? "looks good" : "???"));
  2650.          printf ("   imagefs_paddr = 0x%08x (%d) - set by IPL when startup runs - %s\n", startup_header->imagefs_paddr, startup_header->imagefs_paddr, (startup_header->imagefs_paddr == 0 ? "looks good" : "??? should be zero"));
  2651.          printf ("   imagefs_size  = 0x%08x (%d) - size of uncompressed imagefs\n", startup_header->imagefs_size, startup_header->imagefs_size);
  2652.          printf ("   preboot_size  = 0x%04x (%d) - size of loaded before header - %s\n", startup_header->preboot_size, startup_header->preboot_size, (startup_header->preboot_size == current_offset ? "looks good" : "???"));
  2653.          printf ("   zero0         = 0x%04x (%d) - zeros - %s\n", startup_header->zero0, startup_header->zero0, (startup_header->zero0 == 0 ? "looks good" : "??? should be zero"));
  2654.          printf ("   zero[0]       = 0x%08x (%d) - zeros - %s\n", startup_header->zero[0], startup_header->zero[0], (startup_header->zero[0] == 0 ? "looks good" : "??? should be zero"));
  2655.          printf ("   addr_off      = 0x%016llx (%lld) - offset for startup_vaddr and [image|ram|imagefs]_paddr - %s\n", startup_header->addr_off, startup_header->addr_off, (startup_header->addr_off == 0 ? "looks good" : "??? should be zero"));
  2656.          hex_printf ((uint8_t *) &startup_header->info[0], sizeof (startup_header->info), "   info[48] =\n");
  2657.  
  2658.          // validate that the file can contain up to the startup trailer
  2659.          if (current_offset + startup_header->startup_size > filesize)
  2660.          {
  2661.             printf ("WARNING: this IFS file is corrupted (startup trailer extends past end of file)\n");
  2662.             goto endofdata;
  2663.          }
  2664.  
  2665.          // check if this endianness is ours
  2666.          if (   ( (startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
  2667.              || (!(startup_header->flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
  2668.             is_foreign_endianness = true; // if the header is big endian and we're on a little endian machine, or the other way around, it's a foreign endianness
  2669.          else
  2670.             is_foreign_endianness = false; // else this header is for the same endianness as us
  2671.  
  2672.          // locate the right startup trailer at the right offset
  2673.          if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
  2674.          {
  2675.             startuptrailer_offset = current_offset + startup_header->startup_size - sizeof (startup_trailer_v2_t);
  2676.             startup_trailer_v2 = (startup_trailer_v2_t *) &filedata[startuptrailer_offset];
  2677.             startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t);
  2678.          }
  2679.          else // old V1 trailer
  2680.          {
  2681.             startuptrailer_offset = current_offset + startup_header->startup_size - sizeof (startup_trailer_v1_t);
  2682.             startup_trailer_v1 = (startup_trailer_v1_t *) &filedata[startuptrailer_offset];
  2683.             startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v1_t);
  2684.          }
  2685.  
  2686.          current_offset += sizeof (startup_header_t); // jump over the startup header and reach the startup blob
  2687.          printf ("\n");
  2688.          printf ("Startup blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
  2689.          printf ("   size 0x%zx (%zd) bytes\n", startupfile_blobsize, startupfile_blobsize);
  2690.          printf ("   checksum %d\n", update_checksum (&filedata[current_offset], startupfile_blobsize, is_foreign_endianness));
  2691.  
  2692.          current_offset += startupfile_blobsize; // jump over the startup blob and reach the startup trailer
  2693.          printf ("\n");
  2694.          printf ("Startup trailer at offset 0x%zx (%zd) - version %d:\n", current_offset, current_offset, (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? 2 : 1));
  2695.          if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
  2696.          {
  2697.             for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
  2698.                sprintf (&recorded_sha512[2 * byte_index], "%02x", startup_trailer_v2->sha512[byte_index]);
  2699.             strcpy (computed_sha512, SHA512 (startup_header, startuptrailer_offset - startupheader_offset, NULL));
  2700.             recorded_checksum = startup_trailer_v2->cksum;
  2701.             computed_checksum = update_checksum (startup_header, startuptrailer_offset + SHA512_DIGEST_LENGTH - startupheader_offset, is_foreign_endianness);
  2702.             printf ("    sha512([0x%zx-0x%zx[) = %s - %s\n", startupheader_offset, startuptrailer_offset, recorded_sha512, (strcasecmp (computed_sha512, recorded_sha512) == 0 ? "GOOD" : "BAD"));
  2703.             printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", startupheader_offset, startuptrailer_offset + SHA512_DIGEST_LENGTH, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
  2704.             if (strcasecmp (computed_sha512, recorded_sha512) != 0)
  2705.                printf ("Computed SHA-512: %s\n", computed_sha512);
  2706.             if (computed_checksum != recorded_checksum)
  2707.                printf ("Computed cksum: 0x%08x\n", computed_checksum);
  2708.          }
  2709.          else // old v1 trailer
  2710.          {
  2711.             recorded_checksum = startup_trailer_v1->cksum;
  2712.             computed_checksum = update_checksum (startup_header, sizeof (startup_header) + startupfile_blobsize, is_foreign_endianness);
  2713.             printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", startupheader_offset, startuptrailer_offset, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
  2714.             if (computed_checksum != recorded_checksum)
  2715.                printf ("Computed cksum: 0x%08x\n", computed_checksum);
  2716.          }
  2717.  
  2718.          current_offset += (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2 ? sizeof (startup_trailer_v2_t) : sizeof (startup_trailer_v1_t)); // now reach the next segment
  2719.       }
  2720.  
  2721.       // else does an image header start here ?
  2722.       else if ((current_offset + sizeof (image_header_t) < filesize) && (memcmp (&filedata[current_offset], "imagefs", 7) == 0))
  2723.       {
  2724.          imageheader_offset = current_offset;
  2725.          image_header = (image_header_t *) &filedata[imageheader_offset];
  2726.  
  2727.          // layout:
  2728.          // [IMAGE HEADER]
  2729.          // [image directory entries]
  2730.          // [smallest file blobs up to KERNEL]
  2731.          // [padding]
  2732.          // [KERNEL]
  2733.          // [rest of file blobs]
  2734.          // [IMAGE FOOTER]
  2735.  
  2736.          printf ("\n");
  2737.          printf ("Image header at offset %zx (%zd):\n", current_offset, current_offset);
  2738.          printf ("   signature    = %02x %02x %02x %02x %02x %02x %02x (\"%.7s\") - good\n", image_header->signature[0], image_header->signature[1], image_header->signature[2], image_header->signature[3], image_header->signature[4], image_header->signature[5], image_header->signature[6], image_header->signature);
  2739.          printf ("   flags        = 0x%02x (%s)\n", image_header->flags, describe_uint8 (image_header->flags, imageheader_flags_strings));
  2740.          printf ("   image_size   = 0x%08x (%d) - size from header to end of trailer - %s\n", image_header->image_size, image_header->image_size, (current_offset + image_header->image_size <= filesize ? "looks good" : "BAD (IFS file too short)"));
  2741.          printf ("   hdr_dir_size = 0x%08x (%d) - size from header to last dirent - %s\n", image_header->hdr_dir_size, image_header->hdr_dir_size, (current_offset + image_header->hdr_dir_size < filesize ? "looks good" : "BAD (IFS file too short)"));
  2742.          printf ("   dir_offset   = 0x%08x (%d) - offset from header to first dirent - %s\n", image_header->dir_offset, image_header->dir_offset, (current_offset + image_header->dir_offset >= filesize ? "BAD (IFS file too short)" : (image_header->dir_offset > image_header->hdr_dir_size ? "BAD" : "looks good")));
  2743.          printf ("   boot_ino[4]  = { 0x%08x, 0x%08x, 0x%08x, 0x%08x }\n", image_header->boot_ino[0], image_header->boot_ino[1], image_header->boot_ino[2], image_header->boot_ino[3]);
  2744.          printf ("   script_ino   = 0x%08x (%d) - inode of compiled bootscript\n", image_header->script_ino, image_header->script_ino);
  2745.          printf ("   chain_paddr  = 0x%08x (%d) - offset to next fs signature\n", image_header->chain_paddr, image_header->chain_paddr);
  2746.          hex_printf ((uint8_t *) &image_header->spare[0], sizeof (image_header->spare), "   spare[10] =\n");
  2747.          printf ("   mountflags   = 0x%08x (%s %s %s %s)\n", image_header->mountflags, BINARY (((uint8_t *) &image_header->mountflags)[0]), BINARY (((uint8_t *) &image_header->mountflags)[1]), BINARY (((uint8_t *) &image_header->mountflags)[2]), BINARY (((uint8_t *) &image_header->mountflags)[3]));
  2748.          printf ("   mountpoint   = \"%s\"\n", image_header->mountpoint);
  2749.  
  2750.          // validate that the file can contain up to the image trailer
  2751.          if (current_offset + image_header->image_size > filesize)
  2752.          {
  2753.             printf ("WARNING: this IFS file is corrupted (image trailer extends past end of file)\n");
  2754.             goto endofdata;
  2755.          }
  2756.  
  2757.          // check if this endianness is ours
  2758.          if (   ( (image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
  2759.              || (!(image_header->flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
  2760.             is_foreign_endianness = true; // if the header is big endian and we're on a little endian machine, or the other way around, it's a foreign endianness
  2761.          else
  2762.             is_foreign_endianness = false; // else this header is for the same endianness as us
  2763.  
  2764.          // locate the image trailer at the right offset
  2765.          if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
  2766.          {
  2767.             imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v2_t);
  2768.             image_trailer_v2 = (image_trailer_v2_t *) &filedata[imagetrailer_offset];
  2769.          }
  2770.          else // old V1 trailer
  2771.          {
  2772.             imagetrailer_offset = current_offset + image_header->image_size - sizeof (image_trailer_v1_t);
  2773.             image_trailer_v1 = (image_trailer_v1_t *) &filedata[imagetrailer_offset];
  2774.          }
  2775.  
  2776.          current_offset += sizeof (image_header_t); // jump over the image header and reach the first directory entry
  2777.  
  2778.          // there may be padding before the first directory entry
  2779.          if (image_header->dir_offset - sizeof (image_header_t) > 0)
  2780.             hex_printf (&filedata[current_offset], image_header->dir_offset - sizeof (image_header_t), "\n" "%zd padding bytes at offset 0x%zd (%zd):\n", image_header->dir_offset - sizeof (image_header_t), current_offset, current_offset);
  2781.          current_offset += image_header->dir_offset - sizeof (image_header_t); // padding was processed, jump over it
  2782.  
  2783.          // dump all directory entries until the last one included
  2784.          fsentries = NULL;
  2785.          fsentry_count = 0;
  2786.          while (current_offset < imageheader_offset + image_header->hdr_dir_size)
  2787.          {
  2788.             current_fsentry = (fsentry_t *) &filedata[current_offset];
  2789.  
  2790.             if (imageheader_offset + image_header->hdr_dir_size - current_offset < sizeof (current_fsentry->header))
  2791.                break; // end padding reached
  2792.  
  2793.             // stack up the filesystem entry pointers in an array while we read them
  2794.             reallocated_ptr = realloc (fsentries, (fsentry_count + 1) * sizeof (fsentry_t *));
  2795.             WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
  2796.             fsentries = reallocated_ptr;
  2797.             fsentries[fsentry_count] = current_fsentry;
  2798.             fsentry_count++;
  2799.  
  2800.             printf ("\n");
  2801.             printf ("Filesystem entry at offset 0x%zx (%zd) - last one at 0x%zd (%zd):\n", current_offset, current_offset, imageheader_offset + image_header->hdr_dir_size, imageheader_offset + image_header->hdr_dir_size);
  2802.             printf ("   size           = 0x%04x (%d) - size of dirent - %s\n", current_fsentry->header.size, current_fsentry->header.size, ((current_fsentry->header.size > 0) && (current_offset + current_fsentry->header.size < filesize) ? "looks good" : "BAD"));
  2803.             printf ("   extattr_offset = 0x%04x (%d) - %s\n", current_fsentry->header.extattr_offset, current_fsentry->header.extattr_offset, (current_fsentry->header.extattr_offset == 0 ? "no extattr" : "has extattr"));
  2804.             printf ("   ino            = 0x%08x (%d) - inode number (%s%s%s%s)\n", current_fsentry->header.ino, current_fsentry->header.ino, (current_fsentry->header.ino & 0xE0000000 ? "is" : "nothing special"), (current_fsentry->header.ino & IFS_INO_PROCESSED_ELF ? " PROCESSED_ELF" : ""), (current_fsentry->header.ino & IFS_INO_RUNONCE_ELF ? " RUNONCE_ELF" : ""), (current_fsentry->header.ino & IFS_INO_BOOTSTRAP_EXE ? " BOOTSTRAP_EXE" : ""));
  2805.             printf ("   mode           = 0x%08x (%d) - %s (0%o), POSIX permissions 0%o\n", current_fsentry->header.mode, current_fsentry->header.mode, (S_ISDIR (current_fsentry->header.mode) ? "directory" : (S_ISREG (current_fsentry->header.mode) ? "file" : (S_ISLNK (current_fsentry->header.mode) ? "symlink" : "device"))), (current_fsentry->header.mode & 0xF000) >> 12, current_fsentry->header.mode & 0xFFF);
  2806.             printf ("   gid            = 0x%08x (%d) - owner group ID%s\n", current_fsentry->header.gid, current_fsentry->header.gid, (current_fsentry->header.gid == 0 ? " (root)" : ""));
  2807.             printf ("   uid            = 0x%08x (%d) - owner user ID%s\n", current_fsentry->header.uid, current_fsentry->header.uid, (current_fsentry->header.uid == 0 ? " (root)" : ""));
  2808.             mtime = (time_t) current_fsentry->header.mtime;
  2809.             printf ("   mtime          = 0x%08x (%d) - POSIX timestamp: %s", current_fsentry->header.mtime, current_fsentry->header.mtime, asctime (localtime (&mtime))); // NOTE: asctime() provides the newline
  2810.             if (S_ISDIR (current_fsentry->header.mode))
  2811.                printf ("   [DIRECTORY] path = \"%s\"\n", (char *) &current_fsentry->u.dir.path); // convert from pointer to char array
  2812.             else if (S_ISREG (current_fsentry->header.mode))
  2813.             {
  2814.                printf ("   [FILE] offset = 0x%08x (%d) - %s\n", current_fsentry->u.file.offset, current_fsentry->u.file.offset, (imageheader_offset + current_fsentry->u.file.offset < filesize ? "looks good" : "BAD (IFS file too short)"));
  2815.                printf ("   [FILE] size   = 0x%08x (%d) - %s\n", current_fsentry->u.file.size, current_fsentry->u.file.size, (imageheader_offset + current_fsentry->u.file.offset + current_fsentry->u.file.size < filesize ? "looks good" : "BAD (IFS file too short)"));
  2816.                printf ("   [FILE] path   = \"%s\"\n", (char *) &current_fsentry->u.file.path); // convert from pointer to char array
  2817.             }
  2818.             else if (S_ISLNK (current_fsentry->header.mode))
  2819.             {
  2820.                printf ("   [SYMLINK] sym_offset = 0x%04x (%d) - %s\n", current_fsentry->u.symlink.sym_offset, current_fsentry->u.symlink.sym_offset, (sizeof (current_fsentry->header) + 2 * sizeof (uint16_t) + current_fsentry->u.symlink.sym_offset <= current_fsentry->header.size ? "looks good" : "BAD (dirent too short)"));
  2821.                printf ("   [SYMLINK] sym_size   = 0x%04x (%d) - %s\n", current_fsentry->u.symlink.sym_size, current_fsentry->u.symlink.sym_size, (sizeof (current_fsentry->header) + 2 * sizeof (uint16_t) + current_fsentry->u.symlink.sym_offset + current_fsentry->u.symlink.sym_size <= current_fsentry->header.size ? "looks good" : "BAD (dirent too short)"));
  2822.                printf ("   [SYMLINK] path       = \"%s\"\n", (char *) &current_fsentry->u.symlink.path); // convert from pointer to char array
  2823.                printf ("   [SYMLINK] contents   = \"%s\"\n", ((char *) &current_fsentry->u.symlink.path) + current_fsentry->u.symlink.sym_offset); // convert from pointer to char array
  2824.             }
  2825.             else // can only be a device
  2826.             {
  2827.                printf ("   [DEVICE] dev  = 0x%08x (%d)\n", current_fsentry->u.device.dev, current_fsentry->u.device.dev);
  2828.                printf ("   [DEVICE] rdev = 0x%08x (%d)\n", current_fsentry->u.device.rdev, current_fsentry->u.device.rdev);
  2829.                printf ("   [DEVICE] path = \"%s\"\n", (char *) &current_fsentry->u.device.path); // convert from pointer to char array
  2830.             }
  2831.  
  2832.             if ((current_fsentry->header.size == 0) || (current_offset + current_fsentry->header.size >= filesize))
  2833.             {
  2834.                printf ("WARNING: this IFS file is corrupted (the size of this directory entry is invalid)\n");
  2835.                goto endofdata;
  2836.             }
  2837.  
  2838.             current_offset += current_fsentry->header.size;
  2839.          }
  2840.          if (imageheader_offset + image_header->hdr_dir_size < current_offset + sizeof (current_fsentry->header))
  2841.             hex_printf (&filedata[current_offset], imageheader_offset + image_header->hdr_dir_size - current_offset, "\n" "%zd padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + image_header->hdr_dir_size - current_offset, current_offset, current_offset);
  2842.          current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // padding was processed, jump over it
  2843.  
  2844.          // at this point we are past the directory entries; what is stored now, up to and until the image trailer, is the files' data
  2845.          if (fsentry_count > 0)
  2846.          {
  2847.             while (current_offset < imagetrailer_offset) // and parse data up to the trailer
  2848.             {
  2849.                nearest_distance = SIZE_MAX;
  2850.                nearest_index = SIZE_MAX;
  2851.                for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
  2852.                   if (S_ISREG (fsentries[fsentry_index]->header.mode) // if this directory entry a file (i.e. it has a data blob)...
  2853.                       && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset >= current_offset) // ... AND its data blob is still ahead of our current pointer ...
  2854.                       && (imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset < nearest_distance)) // ... AND it's the closest to us we've found so far
  2855.                   {
  2856.                      nearest_distance = imageheader_offset + (size_t) fsentries[fsentry_index]->u.file.offset - current_offset; // then remember it
  2857.                      nearest_index = fsentry_index;
  2858.                   }
  2859.                if (nearest_index == SIZE_MAX)
  2860.                   break; // found no file ahead, which means we've parsed the whole file data area, so stop the loop so as to proceed to the image trailer
  2861.  
  2862.                fsentry_index = nearest_index;
  2863.                current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
  2864.  
  2865.                // there may be padding before the file data
  2866.                if (imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset > 0)
  2867.                   hex_printf (&filedata[current_offset], imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset, "\n" "%zd padding bytes at offset 0x%zx (%zd):\n", imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset, current_offset, current_offset);
  2868.                current_offset += imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset; // padding was processed, jump over it
  2869.  
  2870.                printf ("\n");
  2871.                printf ("File data blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
  2872.                printf ("   corresponding dirent index: %zd/%zd\n", fsentry_index, fsentry_count);
  2873.                printf ("   corresponding inode 0x%08x (%d) -%s%s%s%s\n", current_fsentry->header.ino, current_fsentry->header.ino, (current_fsentry->header.ino & 0xE0000000 ? "" : " nothing special"), (current_fsentry->header.ino & IFS_INO_PROCESSED_ELF ? " PROCESSED_ELF" : ""), (current_fsentry->header.ino & IFS_INO_RUNONCE_ELF ? " RUNONCE_ELF" : ""), (current_fsentry->header.ino & IFS_INO_BOOTSTRAP_EXE ? " BOOTSTRAP_EXE" : ""));
  2874.                printf ("   corresponding path: \"%s\"\n", (char *) &current_fsentry->u.file.path); // convert from pointer to char array
  2875.                printf ("   size 0x%zx (%zd) bytes\n", (size_t) current_fsentry->u.file.size, (size_t) current_fsentry->u.file.size);
  2876.                if (current_offset + 4 < filesize)
  2877.                   hex_printf (&filedata[current_offset], current_fsentry->u.file.size, "   data:\n");
  2878.                if (current_offset + current_fsentry->u.file.size < filesize)
  2879.                   printf ("   checksum %d\n", update_checksum (&filedata[current_offset], current_fsentry->u.file.size, is_foreign_endianness));
  2880.                else
  2881.                {
  2882.                   printf ("WARNING: this IFS file is corrupted (the size of this file data extends past the IFS size)\n");
  2883.                   goto endofdata;
  2884.                }
  2885.  
  2886.                current_offset += current_fsentry->u.file.size; // now jump over this file's data
  2887.             }
  2888.          }
  2889.  
  2890.          // ad this point we're past the last file data, there may be padding before the image trailer
  2891.          if (imagetrailer_offset - current_offset > 0)
  2892.             hex_printf (&filedata[current_offset], imagetrailer_offset - current_offset, "\n" "%zd padding bytes at offset %zx (%zd):\n", imagetrailer_offset - current_offset, current_offset, current_offset);
  2893.          current_offset += imagetrailer_offset - current_offset; // padding was processed, jump over it
  2894.  
  2895.          printf ("\n");
  2896.          printf ("Image trailer at offset 0x%zx (%zd) - version %d:\n", current_offset, current_offset, (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? 2 : 1));
  2897.          if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
  2898.          {
  2899.             for (byte_index = 0; byte_index < SHA512_DIGEST_LENGTH; byte_index++)
  2900.                sprintf (&recorded_sha512[2 * byte_index], "%02x", image_trailer_v2->sha512[byte_index]);
  2901.             strcpy (computed_sha512, SHA512 (image_header, imagetrailer_offset - imageheader_offset, NULL));
  2902.             recorded_checksum = image_trailer_v2->cksum;
  2903.             computed_checksum = update_checksum (image_header, imagetrailer_offset + SHA512_DIGEST_LENGTH - imageheader_offset, is_foreign_endianness);
  2904.             printf ("    sha512([0x%zx-0x%zx[) = %s - %s\n", imageheader_offset, imagetrailer_offset, recorded_sha512, (strcasecmp (computed_sha512, recorded_sha512) == 0 ? "GOOD" : "BAD"));
  2905.             printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", imageheader_offset, imagetrailer_offset + SHA512_DIGEST_LENGTH, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
  2906.             if (strcasecmp (computed_sha512, recorded_sha512) != 0)
  2907.                printf ("Computed SHA-512: %s\n", computed_sha512);
  2908.             if (computed_checksum != recorded_checksum)
  2909.                printf ("Computed cksum: 0x%08x\n", computed_checksum);
  2910.          }
  2911.          else // old v1 trailer
  2912.          {
  2913.             recorded_checksum = image_trailer_v1->cksum;
  2914.             computed_checksum = update_checksum (image_header, image_header->image_size - sizeof (image_trailer_v1_t), is_foreign_endianness);
  2915.             printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", imageheader_offset, imagetrailer_offset, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
  2916.             if (computed_checksum != recorded_checksum)
  2917.                printf ("Computed cksum: 0x%08x\n", computed_checksum);
  2918.          }
  2919.  
  2920.          current_offset += (image_header->flags & IMAGE_FLAGS_TRAILER_V2 ? sizeof (image_trailer_v2_t) : sizeof (image_trailer_v1_t)); // now reach the next segment (typically end of file)
  2921.       }
  2922.  
  2923.       // else it has to be a boot blob, of which we don't know the size, except that it has to fit in 0xffff bytes and be immediately followed by a startup header
  2924.       else
  2925.       {
  2926.          // so scan for the first startup header magic and version (which makes us 6 bytes to scan for, i.e. "\xeb\x7e\xff\x00" for the magic and "\x01\x00" (LSB) for the version 1)
  2927.          for (byte_index = current_offset; byte_index < filesize - 6; byte_index++)
  2928.             if (memcmp (&filedata[byte_index], "\xeb\x7e\xff\x00" "\x01\x00", 4 + 2) == 0)
  2929.                break; // stop as soon as we find it
  2930.  
  2931.          if (byte_index >= filesize - 6)
  2932.             break; // if not found, stop scanning
  2933.  
  2934.          bootfile_blobsize = byte_index - current_offset;
  2935.          printf ("Boot blob at offset 0x%zx (%zd):\n", current_offset, current_offset);
  2936.          printf ("   size 0x%zx (%zd) bytes\n", bootfile_blobsize, bootfile_blobsize);
  2937.          printf ("   checksum 0x%08x\n", update_checksum (&filedata[current_offset], bootfile_blobsize, false)); // NOTE: endianness is not known yet -- assume same
  2938.  
  2939.          current_offset = byte_index; // now reach the next segment
  2940.       }
  2941.    }
  2942.  
  2943. endofdata:
  2944.    // at this point there's nothing left we're able to parse
  2945.    if (current_offset < filesize)
  2946.    {
  2947.       printf ("End of identifiable data reached.\n");
  2948.       hex_printf (&filedata[current_offset], filesize - current_offset, "\n" "%zd extra bytes at offset %zx (%zd):\n", filesize - current_offset, current_offset, current_offset);
  2949.    }
  2950.  
  2951.    printf ("End of file reached at offset 0x%zx (%zd)\n", filesize, filesize);
  2952.    printf ("IFS dissecation complete.\n");
  2953.    return (0);
  2954. }
  2955.