Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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

Rev Author Line No. Line
1 pmbaty 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>
2 pmbaty 6
#include <stdarg.h>
1 pmbaty 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>
7 pmbaty 17
#define __x86_64__ 1
5 pmbaty 18
#define __ORDER_BIG_ENDIAN__    4321
1 pmbaty 19
#define __ORDER_LITTLE_ENDIAN__ 1234
20
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
21
#define __attribute__(x)
7 pmbaty 22
#define __builtin_bswap16(x) _byteswap_ushort ((unsigned short) (x))
5 pmbaty 23
#define __builtin_bswap32(x) _byteswap_ulong ((unsigned long) (x))
1 pmbaty 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))
2 pmbaty 31
#define strcasecmp(s1,s2) _stricmp ((s1), (s2))
1 pmbaty 32
#define fseek(fp,off,m) _fseeki64 ((fp), (off), (m))
33
#define access(p,m) _access ((p), (m))
34
#define MAXPATHLEN 1024
8 pmbaty 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
1 pmbaty 38
#else // !_MSC_VER
39
#include <sys/param.h>
40
#include <unistd.h>
8 pmbaty 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
1 pmbaty 44
#endif // _MSC_VER
45
 
46
 
4 pmbaty 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);
7 pmbaty 48
#ifndef VERSION_ARG_YYYYMMDD
4 pmbaty 49
#define BUILDDATE_YEAR  (&__DATE__[7]) // compiler will optimize this into a const string, e.g. "2021"
7 pmbaty 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"
4 pmbaty 93
#define VERSION_FMT_YYYYMMDD "%04s%02s%02s"
94
#define VERSION_ARG_YYYYMMDD BUILDDATE_YEAR, BUILDDATE_MONTH, BUILDDATE_DAY
7 pmbaty 95
#endif // !VERSION_ARG_YYYYMMDD
4 pmbaty 96
 
97
 
8 pmbaty 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
 
1 pmbaty 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 ';'
7 pmbaty 116
#define PATH_SEP_STR ";"
1 pmbaty 117
#else // !_WIN32, thus POSIX
118
#define IS_DIRSEP(c) ((c) == '/')
119
#define PATH_SEP ':'
7 pmbaty 120
#define PATH_SEP_STR ":"
1 pmbaty 121
#endif // _WIN32
122
#define RECORD_SEP '\x1e' // ASCII record separator
123
#define RECORD_SEP_STR "\x1e" // ASCII record separator (as string)
124
 
9 pmbaty 125
// macros for accessing ELF files
7 pmbaty 126
#define ELF_MAGIC_STR "\x7f" "ELF"
10 pmbaty 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
7 pmbaty 132
#define ELF_DT_NULL    0 // marks end of dynamic section
133
#define ELF_DT_SONAME 14 // canonical name of shared object
10 pmbaty 134
#define ELF_GET_NUMERIC(elfhdr,elfstruct,member) ((elfhdr)->u.elf.platform_size == 2 ? /* is it a 64-bit ELF file ? */ \
9 pmbaty 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
10 pmbaty 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
9 pmbaty 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
7 pmbaty 167
 
10 pmbaty 168
 
169
 
1 pmbaty 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
 
7 pmbaty 213
#ifdef _MSC_VER
214
#pragma pack(push)
215
#pragma pack(1)
216
#endif // _MSC_VER
217
 
218
 
1 pmbaty 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 */ \
7 pmbaty 311
   "\x34\x00" /*size*/ "\x04" /*type*/ "\x00" /*spare*/ "/proc/boot/ldqnx-64.so.2\0" "/usr/lib/ldqnx-64.so.2\0" \
1 pmbaty 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
 
2 pmbaty 320
typedef struct __attribute__((packed)) fsentry_s
1 pmbaty 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;
2 pmbaty 332
   union __attribute__((packed)) fsentry_specific_u
1 pmbaty 333
   {
2 pmbaty 334
      struct __attribute__((packed)) fsentry_file_s // when (mode & S_IFMT) == S_IFREG
1 pmbaty 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;
2 pmbaty 341
      struct __attribute__((packed)) fsentry_dir_s // when (mode & S_IFMT) == S_IFDIR
1 pmbaty 342
      {
343
         char *path; // null terminated path (no leading slash)
344
      } dir;
2 pmbaty 345
      struct __attribute__((packed)) fsentry_symlink_s // when (mode & S_IFMT) == S_IFLNK
1 pmbaty 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;
2 pmbaty 352
      struct __attribute__((packed)) fsentry_device_s // when (mode & S_IFMT) == S_IF<CHR|BLK|FIFO|NAM|SOCK>
1 pmbaty 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
2 pmbaty 393
} startup_trailer_v1_t;
1 pmbaty 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
6 pmbaty 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)
1 pmbaty 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
2 pmbaty 425
} image_trailer_v1_t; // NOTE: this is the same structure as startup_trailer_v1_t
1 pmbaty 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
2 pmbaty 435
} image_trailer_v2_t; // NOTE: this is the same structure as startup_trailer_v2_t
1 pmbaty 436
 
437
 
7 pmbaty 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
      {
10 pmbaty 445
         uint8_t magic[4];                     // offset 0: "\x7f" + "ELF"
7 pmbaty 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;
10 pmbaty 455
      struct __attribute__((packed)) // size == 52
7 pmbaty 456
      {
10 pmbaty 457
         uint8_t magic[4];                     // offset 0: "\x7f" + "ELF"
7 pmbaty 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
10 pmbaty 476
      } elf32; // size == 52
477
      struct __attribute__((packed)) // size == 64
7 pmbaty 478
      {
10 pmbaty 479
         uint8_t magic[4];                     // offset 0: "\x7f" + "ELF"
7 pmbaty 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
10 pmbaty 498
      } elf64; // size == 64
7 pmbaty 499
   } u;
500
} elf_header_t;
501
 
502
 
10 pmbaty 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
 
7 pmbaty 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
   {
10 pmbaty 543
      struct __attribute__((packed)) // size == 40
7 pmbaty 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
10 pmbaty 555
      } elf32; // size == 40
556
      struct __attribute__((packed)) // size == 64
7 pmbaty 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
10 pmbaty 568
      } elf64; // size == 64
7 pmbaty 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
   {
10 pmbaty 578
      struct __attribute__((packed)) // size == 8
7 pmbaty 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;
10 pmbaty 586
      } elf32; // size == 8
587
      struct __attribute__((packed)) // size == 16
7 pmbaty 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;
10 pmbaty 595
      } elf64; // size == 16
7 pmbaty 596
   } u;
597
} elf_dynamic_section_entry_t;
598
 
599
 
1 pmbaty 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
7 pmbaty 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)
1 pmbaty 614
   char prefix[MAXPATHLEN]; // install path (e.g. "proc/boot")
7 pmbaty 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
2 pmbaty 617
   bool is_compiled_bootscript; // entry has [+script] attribute
10 pmbaty 618
   int extra_ino_flags; // bitmap of extra inode flags (IFS_INO_xxx)
1 pmbaty 619
   char search[10 * MAXPATHLEN]; // binary search path (the default one will be constructed at startup)
2 pmbaty 620
 
7 pmbaty 621
   uint8_t *data;
622
   size_t datalen;
1 pmbaty 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;
7 pmbaty 635
#if defined(__x86_64__)
1 pmbaty 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)
7 pmbaty 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
1 pmbaty 642
static char *buildfile_pathname = NULL; // pathname of IFS build file
7 pmbaty 643
static char *current_line = NULL; // copy of current line in IFS build file
1 pmbaty 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
7 pmbaty 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.
1 pmbaty 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())
7 pmbaty 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
1 pmbaty 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)
2 pmbaty 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
7 pmbaty 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)
1 pmbaty 661
static int fwrite_filecontents (const char *pathname, FILE *fp); // dumps the contents of pathname into fp
10 pmbaty 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
1 pmbaty 664
static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp); // writes the given filesystem entry into fp (without its contents)
7 pmbaty 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
1 pmbaty 666
static int fsentry_compare_pathnames_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by pathnames
7 pmbaty 667
static void update_MKIFS_PATH (const char *processor);
10 pmbaty 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
1 pmbaty 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
 
2 pmbaty 894
static uint8_t *SHA512 (void *data, size_t data_len, uint8_t *digest_or_NULL)
1 pmbaty 895
{
2 pmbaty 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
1 pmbaty 898
 
8 pmbaty 899
   static thread_local uint8_t static_digest[SHA512_DIGEST_LENGTH] = "";
900
   static thread_local char digest_as_string[2 * SHA512_DIGEST_LENGTH + 1] = "";
2 pmbaty 901
 
1 pmbaty 902
   SHA512_CTX ctx;
2 pmbaty 903
   size_t byte_index;
904
 
1 pmbaty 905
   SHA512_Init (&ctx);
906
   SHA512_Update (&ctx, data, data_len);
2 pmbaty 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);
1 pmbaty 914
}
915
 
916
 
7 pmbaty 917
static int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness)
1 pmbaty 918
{
6 pmbaty 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
1 pmbaty 920
 
5 pmbaty 921
   uint8_t accumulator[4] = { 0, 0, 0, 0 };
7 pmbaty 922
   const char *current_char_ptr;
923
   int32_t image_cksum;
5 pmbaty 924
   size_t i;
925
 
7 pmbaty 926
   image_cksum = 0;
927
   current_char_ptr = data;
5 pmbaty 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)
8 pmbaty 933
            image_cksum += (accumulator[3] << 0) + (accumulator[2] << 8) + (accumulator[1] << 16) + (accumulator[0] << 24);
5 pmbaty 934
         else
8 pmbaty 935
            image_cksum += (accumulator[0] << 0) + (accumulator[1] << 8) + (accumulator[2] << 16) + (accumulator[3] << 24);
5 pmbaty 936
      current_char_ptr++;
937
   }
938
 
939
   return (is_foreign_endianness ? __builtin_bswap32 (-image_cksum) : -image_cksum);
940
}
941
 
942
 
1 pmbaty 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
 
2 pmbaty 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
 
8 pmbaty 999
   static thread_local char outstr[9] = "00000000";
2 pmbaty 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
 
8 pmbaty 1010
   static thread_local char *default_bitstrings[8] = { "bit0", "bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7" };
1011
   static thread_local char outstr[8 * 64] = "";
2 pmbaty 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
 
7 pmbaty 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
 
8 pmbaty 1029
   static thread_local char final_pathname[MAXPATHLEN];
7 pmbaty 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
8 pmbaty 1041
      token = (*search_path != 0 ? search_path : NULL);
1042
      nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP_STR)] : NULL);
7 pmbaty 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
 
1 pmbaty 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);
8 pmbaty 1114
   fflush (fp); // force flush to disk, because the C stream API is *buffered*
1 pmbaty 1115
   free (blob_buffer);
1116
   return (ret);
1117
}
1118
 
1119
 
10 pmbaty 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
 
1 pmbaty 1158
static size_t fwrite_fsentry (const fsentry_t *fsentry, FILE *fp)
1159
{
7 pmbaty 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
 
1 pmbaty 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)
8 pmbaty 1170
      fwrite_or_die (&fsentry->header, 1, sizeof (fsentry->header), fp); // write the entry header (PACKED STRUCT)
1 pmbaty 1171
   count += sizeof (fsentry->header);
1172
   if (S_ISREG (fsentry->header.mode))
1173
   {
1174
      if (fp != NULL)
1175
      {
8 pmbaty 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
1 pmbaty 1178
      }
1179
      count += 2 * sizeof (uint32_t);
1180
      datalen = strlen (fsentry->u.file.path) + 1;
1181
      if (fp != NULL)
8 pmbaty 1182
         fwrite_or_die (fsentry->u.file.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash)
1 pmbaty 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)
8 pmbaty 1189
         fwrite_or_die (fsentry->u.dir.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash)
1 pmbaty 1190
      count += datalen;
1191
   }
1192
   else if (S_ISLNK (fsentry->header.mode))
1193
   {
1194
      if (fp != NULL)
1195
      {
8 pmbaty 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
1 pmbaty 1198
      }
1199
      count += 2 * sizeof (uint16_t);
1200
      datalen = strlen (fsentry->u.symlink.path) + 1;
1201
      if (fp != NULL)
8 pmbaty 1202
         fwrite_or_die (fsentry->u.symlink.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash)
1 pmbaty 1203
      count += datalen;
1204
      datalen = strlen (fsentry->u.symlink.contents) + 1;
1205
      if (fp != NULL)
8 pmbaty 1206
         fwrite_or_die (fsentry->u.symlink.contents, 1, (size_t) datalen, fp); // write null-terminated symlink contents
1 pmbaty 1207
      count += datalen;
1208
   }
1209
   else
1210
   {
1211
      if (fp != NULL)
1212
      {
8 pmbaty 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
1 pmbaty 1215
      }
1216
      count += 2 * sizeof (uint32_t);
1217
      datalen = strlen (fsentry->u.device.path) + 1;
1218
      if (fp != NULL)
8 pmbaty 1219
         fwrite_or_die (fsentry->u.device.path, 1, (size_t) datalen, fp); // write null-terminated path (no leading slash)
1 pmbaty 1220
      count += datalen;
1221
   }
1222
 
7 pmbaty 1223
   if (count < fsentry->header.size)
1224
   {
1225
      if (fp != NULL)
8 pmbaty 1226
         fwrite_or_die (zeropad_buffer, 1, fsentry->header.size - count, fp); // pad as necessary
7 pmbaty 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
   }
1 pmbaty 1234
 
1235
   return (count);
1236
}
1237
 
1238
 
7 pmbaty 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)
1 pmbaty 1240
{
10 pmbaty 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
 
8 pmbaty 1250
   static thread_local char candidate_pathname[1024];
1 pmbaty 1251
   static int inode_count = 0; // will be preincremented each time this function is called
1252
 
7 pmbaty 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;
10 pmbaty 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;
7 pmbaty 1265
   const char *canonical_dylib_name;
1266
   const char *dynamic_strings; // strings table of the ".dynamic" section
1267
   const char *last_dirsep;
10 pmbaty 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;
7 pmbaty 1281
   char *resolved_pathname;
1 pmbaty 1282
   void *reallocated_ptr;
7 pmbaty 1283
   void *old_data;
1 pmbaty 1284
   struct stat stat_buf;
10 pmbaty 1285
   size_t new_shdrtable_offset;
1286
   size_t end_padding_offset;
7 pmbaty 1287
   size_t table_index;
1288
   size_t table_count;
10 pmbaty 1289
   size_t name_len;
1 pmbaty 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
   {
7 pmbaty 1298
      if (strcmp (stored_pathname, "/proc/boot/boot") == 0) // is it the kernel ?
1 pmbaty 1299
      {
7 pmbaty 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;
10 pmbaty 1304
         entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // procnto needs to have these flags stamped on the inode
8 pmbaty 1305
         entry_parms->st_mode = S_IFREG | 0700; // procnto requires 0700 permissions
10 pmbaty 1306
         image_kernel_ino = entry_parms->extra_ino_flags | (inode_count + 1);
7 pmbaty 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
2 pmbaty 1310
 
7 pmbaty 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;
10 pmbaty 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);
7 pmbaty 1316
      }
8 pmbaty 1317
      else if (buildhost_pathname != NULL) // else was a source file pathname supplied ?
1 pmbaty 1318
      {
8 pmbaty 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
7 pmbaty 1320
         if (resolved_pathname == NULL)
1 pmbaty 1321
         {
7 pmbaty 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);
1 pmbaty 1324
         }
7 pmbaty 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);
8 pmbaty 1329
      }
1 pmbaty 1330
 
9 pmbaty 1331
      // is the file we're storing an ELF file ?
7 pmbaty 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)
10 pmbaty 1334
          && (memcmp (ELF_GET_STRING (elf, elf, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic
7 pmbaty 1335
      {
9 pmbaty 1336
         // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ?
10 pmbaty 1337
         if ((ELF_GET_NUMERIC (elf, elf, type) == 3) && entry_parms->should_autosymlink_dylib)
7 pmbaty 1338
         {
9 pmbaty 1339
            // we need to find the SONAME of this library
1340
            canonical_dylib_name = NULL;
1 pmbaty 1341
 
9 pmbaty 1342
            // locate the sections we need (the dynamic section and its strings table)
10 pmbaty 1343
            shdr_dynamic = elf_get_section_header_by_name (elf, ".dynamic");
1344
            shdr_dynstr = elf_get_section_header_by_name (elf, ".dynstr");
7 pmbaty 1345
 
9 pmbaty 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
            {
10 pmbaty 1349
               dynamic_strings = (char *) &entry_parms->data[ELF_GET_NUMERIC (elf, shdr_dynstr, file_offset)]; // quick access to dynamic sections strings table
7 pmbaty 1350
 
9 pmbaty 1351
               // walk through the dynamic section, look for the DT_SONAME entry
10 pmbaty 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);
9 pmbaty 1354
                    dynamic_entry = (elf_dynamic_section_entry_t *) ((uint8_t *) dynamic_entry + ELF_STRUCT_SIZE (elf, dynamic_entry)))
10 pmbaty 1355
                  if (ELF_GET_NUMERIC (elf, dynamic_entry, tag) == ELF_DT_SONAME)
9 pmbaty 1356
                  {
10 pmbaty 1357
                     canonical_dylib_name = dynamic_strings + ELF_GET_NUMERIC (elf, dynamic_entry, value.as_integer);
9 pmbaty 1358
                     break;
1359
                  }
1360
 
1361
               // do we have it ?
1362
               if ((canonical_dylib_name != NULL) && (canonical_dylib_name[0] != 0))
7 pmbaty 1363
               {
9 pmbaty 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
                  }
7 pmbaty 1370
               }
1 pmbaty 1371
            }
9 pmbaty 1372
         } // end if the file we're storing is a dylib
10 pmbaty 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
9 pmbaty 1638
      } // end if the file we're storing is an ELF file
1 pmbaty 1639
   }
1640
   else if (S_ISLNK (entry_parms->st_mode)) // else are we storing a symbolic link ?
7 pmbaty 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);
1 pmbaty 1642
   else // we must be storing a FIFO
1643
   {
7 pmbaty 1644
      if (strchr (entry_parms->data, ':') == NULL)
1 pmbaty 1645
      {
1646
         fprintf (stderr, "fatal error: device entry \"%s\" malformed (no 'dev:rdev' pair)\n", stored_pathname);
1647
         exit (1);
1648
      }
7 pmbaty 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);
1 pmbaty 1650
   }
1651
 
7 pmbaty 1652
   // grow filesystem entries array to hold one more slot
1 pmbaty 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;
10 pmbaty 1663
   fsentry->header.ino = entry_parms->extra_ino_flags | (++inode_count);
1 pmbaty 1664
   fsentry->header.mode = entry_parms->st_mode;
1665
   fsentry->header.gid = entry_parms->gid;
1666
   fsentry->header.uid = entry_parms->uid;
7 pmbaty 1667
   fsentry->header.mtime = (entry_parms->mtime == UINT32_MAX ? (uint32_t) time (NULL) : entry_parms->mtime);
1 pmbaty 1668
   if (S_ISDIR (entry_parms->st_mode))
1669
   {
7 pmbaty 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
1 pmbaty 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
7 pmbaty 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);
8 pmbaty 1680
      WELLMANNERED_ASSERT (fsentry->u.file.UNSAVED_databuf, "out of memory");
7 pmbaty 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
1 pmbaty 1683
      fsentry->UNSAVED_was_data_written = false; // there *IS* data to save
1684
   }
1685
   else if (S_ISLNK (entry_parms->st_mode))
1686
   {
7 pmbaty 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);
8 pmbaty 1691
      WELLMANNERED_ASSERT (fsentry->u.symlink.contents, "out of memory");
7 pmbaty 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
1 pmbaty 1693
      fsentry->UNSAVED_was_data_written = true; // no data to save
1694
   }
7 pmbaty 1695
   else // necessarily a device node
1 pmbaty 1696
   {
7 pmbaty 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
1 pmbaty 1701
      fsentry->UNSAVED_was_data_written = true; // no data to save
1702
   }
1703
   (*fsentry_count)++;
7 pmbaty 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 !
10 pmbaty 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
7 pmbaty 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
 
1 pmbaty 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
 
3 pmbaty 1729
   const fsentry_t *entry_a = (const fsentry_t *) a;
1730
   const fsentry_t *entry_b = (const fsentry_t *) b;
1 pmbaty 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
 
7 pmbaty 1737
static void update_MKIFS_PATH (const char *processor)
1 pmbaty 1738
{
7 pmbaty 1739
   // updates the value of MKIFS_PATH according to the passed processor name string, unless an environment variable already defines it
1 pmbaty 1740
 
7 pmbaty 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
8 pmbaty 1763
      WELLMANNERED_ASSERT (MKIFS_PATH, "out of memory");
7 pmbaty 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;
1 pmbaty 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
 
2 pmbaty 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
7 pmbaty 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]
10 pmbaty 1804
      .extra_ino_flags = 0,
7 pmbaty 1805
      .search = "",
1806
      .data = NULL,
1807
      .datalen = 0
1808
   };
2 pmbaty 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)
1 pmbaty 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
2 pmbaty 1813
   size_t bootfile_size = 0;                 // HACK: size of the bootcode binary blob file to put at the start of a bootable IFS
1 pmbaty 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
 
7 pmbaty 1819
   char path_on_buildhost[MAXPATHLEN] = "";
1820
   char path_in_ifs[MAXPATHLEN] = "";
1 pmbaty 1821
   char *ifs_pathname = NULL;
1822
   void *reallocated_ptr;
10 pmbaty 1823
   const elf_header_t *elf;
1824
   elf_program_header_t *phdr;
7 pmbaty 1825
   struct tm utc_time;
1 pmbaty 1826
   struct stat stat_buf;
1827
   size_t startuptrailer_offset;
1828
   size_t startupheader_offset;
8 pmbaty 1829
   size_t imagetrailer_offset;
1830
   size_t imageheader_offset;
1 pmbaty 1831
   size_t imgdir_offset;
1832
   size_t imgdir_size;
1833
   size_t final_size;
7 pmbaty 1834
   size_t blob_size;
1835
   size_t available_space;
1836
   size_t allocated_size;
1 pmbaty 1837
   size_t fsentry_index;
7 pmbaty 1838
   size_t largest_index;
1839
   size_t largest_size;
1 pmbaty 1840
   size_t curr_offset;
10 pmbaty 1841
   size_t table_index;
1842
   size_t table_count;
8 pmbaty 1843
   uint8_t *blob_data;
1844
   int32_t checksum;
7 pmbaty 1845
   char *specifiedpathname_start;
1 pmbaty 1846
   char *directiveblock_start;
1847
   char *write_ptr;
7 pmbaty 1848
   char *line_ptr;
1 pmbaty 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;
8 pmbaty 1856
   bool has_data_already = false;
1 pmbaty 1857
   bool want_info = false;
10 pmbaty 1858
   bool want_everything = false;
1 pmbaty 1859
   bool want_help = false;
5 pmbaty 1860
   bool is_foreign_endianness;
1 pmbaty 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
      }
7 pmbaty 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
      }
1 pmbaty 1902
      else if (strcmp (argv[arg_index], "--info") == 0)
1903
         want_info = true;
10 pmbaty 1904
      else if (strcmp (argv[arg_index], "--everything") == 0)
1905
         want_everything = true;
1 pmbaty 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 ?
2 pmbaty 1915
   if (want_help || (buildfile_pathname == NULL) || (!want_info && (ifs_pathname == NULL)))
1 pmbaty 1916
   {
4 pmbaty 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)
1 pmbaty 1920
         fprintf (stderr, "error: missing parameters\n");
1921
      fprintf ((want_help ? stdout : stderr), "usage:\n");
7 pmbaty 1922
      fprintf ((want_help ? stdout : stderr), "    ifstool [--bootfile <pathname>] [--startupfile <pathname>@<EP_from_imgbase>] [--kernelfile <pathname>@<fileoffs>] [-n[n]] <buildfile> <outfile>\n");
10 pmbaty 1923
      fprintf ((want_help ? stdout : stderr), "    ifstool --info [--everything] <ifs file>\n");
1 pmbaty 1924
      fprintf ((want_help ? stdout : stderr), "    ifstool --help\n");
7 pmbaty 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");
1 pmbaty 1926
      exit (want_help ? 0 : 1);
1927
   }
1928
 
2 pmbaty 1929
   // do we want info about a particular IFS ? if so, dump it
1930
   if (want_info)
10 pmbaty 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
2 pmbaty 1932
 
1 pmbaty 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
 
7 pmbaty 1946
   // prepare a default MKIFS_PATH assuming the host processor
1947
   update_MKIFS_PATH (image_processor);
1948
 
1 pmbaty 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;
7 pmbaty 1960
   add_fsentry (&fsentries, &fsentry_count, &entry_parms, "", NULL); // add the root dir first
1 pmbaty 1961
 
1962
   while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL)
1963
   {
7 pmbaty 1964
      if (current_line != NULL)
1965
         free (current_line);
1966
      current_line = strdup (line_buffer);
8 pmbaty 1967
      WELLMANNERED_ASSERT (current_line, "out of memory");
1 pmbaty 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));
7 pmbaty 1984
      path_in_ifs[0] = 0;
1985
      path_on_buildhost[0] = 0;
8 pmbaty 1986
      has_data_already = false;
1 pmbaty 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
7 pmbaty 2042
                  update_MKIFS_PATH (image_processor);
1 pmbaty 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.)
2 pmbaty 2046
               //strcpy (image_bootfile, bootfile_pathname); // FIXME: HACK
2047
               if (stat (bootfile_pathname, &stat_buf) != 0)
1 pmbaty 2048
               {
2 pmbaty 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));
1 pmbaty 2050
                  exit (1);
2051
               }
2 pmbaty 2052
               bootfile_size = stat_buf.st_size; // save preboot file size
2053
               fprintf (stderr, "info: processor \"%s\" bootfile \"%s\"\n", image_processor, bootfile_pathname);
8 pmbaty 2054
               if (read_filecontents (kernelfile_pathname, ".", &entry_parms.data, &entry_parms.datalen) == NULL)
2 pmbaty 2055
               {
2056
                  fprintf (stderr, "fatal error: unable to read precompiled kernel file \"%s\" specified in --kernelfile argument\n", kernelfile_pathname);
2057
                  exit (1);
2058
               }
8 pmbaty 2059
               has_data_already = true; // remember we already have data
1 pmbaty 2060
            }
7 pmbaty 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) {
2 pmbaty 2074
               entry_parms.is_compiled_bootscript = true;
7 pmbaty 2075
               entry_parms.data = malloc (sizeof (INITIAL_STARTUP_SCRIPT) - 1);
8 pmbaty 2076
               WELLMANNERED_ASSERT (entry_parms.data, "out of memory");
7 pmbaty 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;
8 pmbaty 2079
               has_data_already = true; // remember we already have data
2 pmbaty 2080
            }
7 pmbaty 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;
1 pmbaty 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)
7 pmbaty 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");
1 pmbaty 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
7 pmbaty 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
1 pmbaty 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
      }
7 pmbaty 2138
      while ((*line_ptr != 0) && ((!is_quoted_context && (*line_ptr != '=') && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"'))))
1 pmbaty 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
7 pmbaty 2150
      if (is_quoted_context && (*line_ptr == '"'))
2151
         line_ptr++; // skip a possible final quote
1 pmbaty 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
         {
7 pmbaty 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
 
1 pmbaty 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
               }
8 pmbaty 2186
               else if ((read_char == '\\') && !is_escaped_char)
1 pmbaty 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
8 pmbaty 2193
                  if (!has_data_already) // only store the contents if we do NOT know the data yet
1 pmbaty 2194
                  {
7 pmbaty 2195
                     if (entry_parms.datalen == allocated_size) // reallocate in 4 kb blocks
1 pmbaty 2196
                     {
7 pmbaty 2197
                        reallocated_ptr = realloc (entry_parms.data, allocated_size + 4096);
8 pmbaty 2198
                        WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
7 pmbaty 2199
                        entry_parms.data = reallocated_ptr;
2200
                        allocated_size += 4096;
1 pmbaty 2201
                     }
7 pmbaty 2202
                     entry_parms.data[entry_parms.datalen++] = read_char;
1 pmbaty 2203
                  }
2204
                  if (read_char == '\n')
8 pmbaty 2205
                     lineno++; // update line counter as we parse the inline content
1 pmbaty 2206
               }
8 pmbaty 2207
            } // end for
2208
            has_data_already = true; // remember we have data now
1 pmbaty 2209
         }
7 pmbaty 2210
         else // not a content definition between { brackets }, must be either a pathname on the build host, or the target of a symlink
1 pmbaty 2211
         {
7 pmbaty 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
            {
8 pmbaty 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
7 pmbaty 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
1 pmbaty 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
 
7 pmbaty 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)
1 pmbaty 2256
      }
2257
 
2258
      // now add this entry to the image filesystem
7 pmbaty 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;
1 pmbaty 2265
 
7 pmbaty 2266
      add_fsentry (&fsentries, &fsentry_count, &entry_parms, path_in_ifs, path_on_buildhost); // and add filesystem entry
1 pmbaty 2267
 
7 pmbaty 2268
      if (entry_parms.data != NULL)
2269
         free (entry_parms.data); // if blob data was allocated, free it
1 pmbaty 2270
   }
2271
 
2272
   // write IFS file
7 pmbaty 2273
   fp = fopen (ifs_pathname, "w+b");
1 pmbaty 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
 
2 pmbaty 2280
   // do we have a startup file ? if so, this is a bootable image
2281
   if (startupfile_pathname != NULL)
1 pmbaty 2282
   {
2283
      // write boot prefix
2 pmbaty 2284
      fwrite_filecontents (bootfile_pathname, fp);
1 pmbaty 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)
10 pmbaty 2294
         startup_header.machine = ELF_MACHINE_X86_64; // EM_X86_64
1 pmbaty 2295
      else if (strcmp (image_processor, "aarch64le") == 0)
10 pmbaty 2296
         startup_header.machine = ELF_MACHINE_AARCH64; // EM_AARCH64
1 pmbaty 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.*")
2 pmbaty 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)
1 pmbaty 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
2 pmbaty 2307
      startup_header.stored_size   = WILL_BE_FILLED_LATER;                                  // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
1 pmbaty 2308
      startup_header.imagefs_size  = WILL_BE_FILLED_LATER;                                  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
2 pmbaty 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))
8 pmbaty 2310
      fwrite_or_die (&startup_header, 1, sizeof (startup_header), fp); // write startup header
1 pmbaty 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
8 pmbaty 2321
      fwrite_or_die (&startup_trailer, 1, sizeof (startup_trailer), fp); // write startup trailer
1 pmbaty 2322
      PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
2323
   }
2324
 
8 pmbaty 2325
   imageheader_offset = ftell (fp); // save image header offset
1 pmbaty 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)
7 pmbaty 2330
   image_header.hdr_dir_size  = WILL_BE_FILLED_LATER; // size from header to last dirent (here 0x12b8 or 4792)
1 pmbaty 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")
8 pmbaty 2335
   fwrite_or_die (&image_header, 1, sizeof (image_header), fp); // write image header
1 pmbaty 2336
   PAD_OUTFILE_TO (ROUND_TO_UPPER_MULTIPLE (ftell (fp), image_align)); // pad as necessary
2337
 
6 pmbaty 2338
   // write image directory (with the wrong file offsets)
1 pmbaty 2339
   imgdir_offset = ftell (fp);
7 pmbaty 2340
   imgdir_size = 0; // measure image dir size on the fly
1 pmbaty 2341
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
7 pmbaty 2342
      imgdir_size += fwrite_fsentry (&fsentries[fsentry_index], fp); // NOTE: padding is handled in this function
1 pmbaty 2343
 
8 pmbaty 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
 
1 pmbaty 2347
   // is it a bootable image with a kernel file ?
2 pmbaty 2348
   if ((startupfile_pathname != NULL) && (kernelfile_pathname != NULL))
1 pmbaty 2349
   {
7 pmbaty 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)
8 pmbaty 2353
            break; // locate the startup script directory entry
7 pmbaty 2354
      if (fsentry_index < fsentry_count) // found it ?
1 pmbaty 2355
      {
2356
         curr_offset = ftell (fp);
2357
         if (curr_offset + fsentries[fsentry_index].u.file.size >= kernelfile_offset)
7 pmbaty 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
         }
8 pmbaty 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
1 pmbaty 2364
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2365
      }
7 pmbaty 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;
8 pmbaty 2375
         largest_size = 0;
7 pmbaty 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
         }
8 pmbaty 2386
         if (largest_size == 0)
7 pmbaty 2387
            break; // found none ? if so, stop searching
2388
 
10 pmbaty 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
 
8 pmbaty 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
7 pmbaty 2403
         fsentries[largest_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2404
      }
10 pmbaty 2405
      fprintf (stderr, "Current offset: 0x%zx\n", curr_offset);
2406
      fprintf (stderr, "Kernel file offset: 0x%zx\n", kernelfile_offset);
1 pmbaty 2407
      PAD_OUTFILE_TO (kernelfile_offset); // reach the kernel offset
2408
 
8 pmbaty 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
1 pmbaty 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
8 pmbaty 2421
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1 pmbaty 2422
   }
2423
 
8 pmbaty 2424
   // then write all the other files by increasing inode number: ELF files first
7 pmbaty 2425
   for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1 pmbaty 2426
   {
8 pmbaty 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
1 pmbaty 2429
         continue; // skip all entries that don't have a separate data block and those who were written already
2430
      curr_offset = ftell (fp);
8 pmbaty 2431
      fsentries[fsentry_index].u.file.offset = (uint32_t) (curr_offset - imageheader_offset); // save file data blob offset in file structure
10 pmbaty 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
 
8 pmbaty 2445
      fwrite_or_die (fsentries[fsentry_index].u.file.UNSAVED_databuf, 1, fsentries[fsentry_index].u.file.size, fp); // write file data blob
1 pmbaty 2446
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2447
   }
8 pmbaty 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
1 pmbaty 2458
 
2459
   // finally, write trailer (including empty checksum)
8 pmbaty 2460
   imagetrailer_offset = ftell (fp); // save image trailer offset
2461
   fwrite_or_die (&image_trailer, 1, sizeof (image_trailer), fp); // write image trailer
1 pmbaty 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
 
2 pmbaty 2475
   // do we have a startup file ? if so, this is a bootable image
2476
   if (startupfile_pathname != NULL)
1 pmbaty 2477
   {
2478
      // rewrite startup header with final values
8 pmbaty 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
   }
1 pmbaty 2486
 
8 pmbaty 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
   {
1 pmbaty 2511
      // compute SHA-512 checksum and V1 checksum of startup block
6 pmbaty 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
1 pmbaty 2517
 
8 pmbaty 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
1 pmbaty 2521
   }
2522
 
2523
   // compute SHA-512 checksum and V1 checksum of image block
5 pmbaty 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
1 pmbaty 2529
 
8 pmbaty 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
1 pmbaty 2533
 
8 pmbaty 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);
1 pmbaty 2538
   fclose (fp);
8 pmbaty 2539
   free (blob_data);
2540
 
2541
   // finished, exit with a success code
1 pmbaty 2542
   fprintf (stdout, "Success\n");
2543
   exit (0);
2544
}
2 pmbaty 2545
 
2546
 
10 pmbaty 2547
static int dump_ifs_info (const char *ifs_pathname, bool want_everything)
2 pmbaty 2548
{
7 pmbaty 2549
   #define hex_printf(buf,size,...) do { \
10 pmbaty 2550
      if (want_everything || ((size) <= 16 * 1024)) /* only print when it's not too big (up to 16 kb) */\
7 pmbaty 2551
         hex_fprintf (stdout, (buf), (size), 16, __VA_ARGS__); /* use 16 columns in hex output to stdout */ \
2552
      else { \
2553
         printf (__VA_ARGS__); \
10 pmbaty 2554
         hex_fprintf (stdout, (buf), 1024, 16, "   first kilobyte:\n"); \
7 pmbaty 2555
      } \
2556
   } while (0)
2 pmbaty 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;
8 pmbaty 2581
   size_t startupheader_offset = 0;
2 pmbaty 2582
   startup_trailer_v1_t *startup_trailer_v1 = NULL;
2583
   startup_trailer_v2_t *startup_trailer_v2 = NULL;
8 pmbaty 2584
   size_t startuptrailer_offset = 0;
2 pmbaty 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;
3 pmbaty 2589
   size_t imagetrailer_offset = 0;
2590
   fsentry_t **fsentries = NULL; // mallocated
2591
   size_t fsentry_count = 0;
2 pmbaty 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;
3 pmbaty 2596
   void *reallocated_ptr;
5 pmbaty 2597
   bool is_foreign_endianness;
2 pmbaty 2598
   size_t bootfile_blobsize = 0;
2599
   size_t current_offset;
3 pmbaty 2600
   size_t fsentry_index;
2601
   size_t nearest_distance;
2602
   size_t nearest_index;
2 pmbaty 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
8 pmbaty 2611
   if (read_filecontents (ifs_pathname, ".", &filedata, &filesize) == NULL)
2 pmbaty 2612
   {
2613
      fprintf (stderr, "error: can't open \"%s\" for reading: %s\n", ifs_pathname, strerror (errno));
2614
      return (1);
2615
   }
2616
 
4 pmbaty 2617
   printf ("QNX In-kernel Filesystem analysis produced by ifstool version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
2 pmbaty 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
      {
8 pmbaty 2627
         startupheader_offset = current_offset;
2628
         startup_header = (startup_header_t *) &filedata[startupheader_offset];
2 pmbaty 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"));
10 pmbaty 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")));
2 pmbaty 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
         {
7 pmbaty 2661
            printf ("WARNING: this IFS file is corrupted (startup trailer extends past end of file)\n");
2662
            goto endofdata;
2 pmbaty 2663
         }
2664
 
5 pmbaty 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
 
2 pmbaty 2672
         // locate the right startup trailer at the right offset
2673
         if (startup_header->flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2)
2674
         {
8 pmbaty 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];
2 pmbaty 2677
            startupfile_blobsize = startup_header->startup_size - sizeof (startup_header_t) - sizeof (startup_trailer_v2_t);
2678
         }
2679
         else // old V1 trailer
2680
         {
8 pmbaty 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];
2 pmbaty 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);
7 pmbaty 2690
         printf ("   checksum %d\n", update_checksum (&filedata[current_offset], startupfile_blobsize, is_foreign_endianness));
2 pmbaty 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]);
8 pmbaty 2699
            strcpy (computed_sha512, SHA512 (startup_header, startuptrailer_offset - startupheader_offset, NULL));
2 pmbaty 2700
            recorded_checksum = startup_trailer_v2->cksum;
8 pmbaty 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"));
9 pmbaty 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"));
2 pmbaty 2704
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
2705
               printf ("Computed SHA-512: %s\n", computed_sha512);
2706
            if (computed_checksum != recorded_checksum)
9 pmbaty 2707
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
2 pmbaty 2708
         }
2709
         else // old v1 trailer
2710
         {
2711
            recorded_checksum = startup_trailer_v1->cksum;
7 pmbaty 2712
            computed_checksum = update_checksum (startup_header, sizeof (startup_header) + startupfile_blobsize, is_foreign_endianness);
9 pmbaty 2713
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", startupheader_offset, startuptrailer_offset, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
2 pmbaty 2714
            if (computed_checksum != recorded_checksum)
9 pmbaty 2715
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
2 pmbaty 2716
         }
2717
 
3 pmbaty 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
2 pmbaty 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]
6 pmbaty 2730
         // [smallest file blobs up to KERNEL]
2 pmbaty 2731
         // [padding]
2732
         // [KERNEL]
6 pmbaty 2733
         // [rest of file blobs]
2 pmbaty 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
         {
7 pmbaty 2753
            printf ("WARNING: this IFS file is corrupted (image trailer extends past end of file)\n");
2754
            goto endofdata;
2 pmbaty 2755
         }
2756
 
5 pmbaty 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
 
2 pmbaty 2764
         // locate the image trailer at the right offset
2765
         if (image_header->flags & IMAGE_FLAGS_TRAILER_V2)
3 pmbaty 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
         }
2 pmbaty 2770
         else // old V1 trailer
3 pmbaty 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
         }
2 pmbaty 2775
 
2776
         current_offset += sizeof (image_header_t); // jump over the image header and reach the first directory entry
3 pmbaty 2777
 
2778
         // there may be padding before the first directory entry
2 pmbaty 2779
         if (image_header->dir_offset - sizeof (image_header_t) > 0)
6 pmbaty 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);
3 pmbaty 2781
         current_offset += image_header->dir_offset - sizeof (image_header_t); // padding was processed, jump over it
2 pmbaty 2782
 
2783
         // dump all directory entries until the last one included
3 pmbaty 2784
         fsentries = NULL;
2785
         fsentry_count = 0;
6 pmbaty 2786
         while (current_offset < imageheader_offset + image_header->hdr_dir_size)
2 pmbaty 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
 
3 pmbaty 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 *));
8 pmbaty 2795
            WELLMANNERED_ASSERT (reallocated_ptr, "out of memory");
3 pmbaty 2796
            fsentries = reallocated_ptr;
2797
            fsentries[fsentry_count] = current_fsentry;
2798
            fsentry_count++;
2799
 
2 pmbaty 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);
7 pmbaty 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"));
2 pmbaty 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)"));
3 pmbaty 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)"));
2 pmbaty 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)"));
3 pmbaty 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)"));
2 pmbaty 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
 
7 pmbaty 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
 
2 pmbaty 2838
            current_offset += current_fsentry->header.size;
2839
         }
3 pmbaty 2840
         if (imageheader_offset + image_header->hdr_dir_size < current_offset + sizeof (current_fsentry->header))
6 pmbaty 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);
3 pmbaty 2842
         current_offset += imageheader_offset + image_header->hdr_dir_size - current_offset; // padding was processed, jump over it
2 pmbaty 2843
 
3 pmbaty 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
2 pmbaty 2861
 
3 pmbaty 2862
               fsentry_index = nearest_index;
2863
               current_fsentry = fsentries[fsentry_index]; // quick access to closest fsentry
2 pmbaty 2864
 
3 pmbaty 2865
               // there may be padding before the file data
2866
               if (imageheader_offset + (size_t) current_fsentry->u.file.offset - current_offset > 0)
6 pmbaty 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);
3 pmbaty 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);
6 pmbaty 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" : ""));
3 pmbaty 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);
7 pmbaty 2876
               if (current_offset + 4 < filesize)
10 pmbaty 2877
                  hex_printf (&filedata[current_offset], current_fsentry->u.file.size, "   data:\n");
7 pmbaty 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
               }
3 pmbaty 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)
6 pmbaty 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);
3 pmbaty 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]);
8 pmbaty 2901
            strcpy (computed_sha512, SHA512 (image_header, imagetrailer_offset - imageheader_offset, NULL));
3 pmbaty 2902
            recorded_checksum = image_trailer_v2->cksum;
8 pmbaty 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"));
9 pmbaty 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"));
3 pmbaty 2906
            if (strcasecmp (computed_sha512, recorded_sha512) != 0)
2907
               printf ("Computed SHA-512: %s\n", computed_sha512);
2908
            if (computed_checksum != recorded_checksum)
9 pmbaty 2909
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
3 pmbaty 2910
         }
2911
         else // old v1 trailer
2912
         {
2913
            recorded_checksum = image_trailer_v1->cksum;
7 pmbaty 2914
            computed_checksum = update_checksum (image_header, image_header->image_size - sizeof (image_trailer_v1_t), is_foreign_endianness);
9 pmbaty 2915
            printf ("    cksum([0x%zx-0x%zx[) = 0x%08x - %s\n", imageheader_offset, imagetrailer_offset, recorded_checksum, (computed_checksum == recorded_checksum ? "GOOD" : "BAD"));
3 pmbaty 2916
            if (computed_checksum != recorded_checksum)
9 pmbaty 2917
               printf ("Computed cksum: 0x%08x\n", computed_checksum);
3 pmbaty 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)
2 pmbaty 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);
7 pmbaty 2937
         printf ("   checksum 0x%08x\n", update_checksum (&filedata[current_offset], bootfile_blobsize, false)); // NOTE: endianness is not known yet -- assume same
2 pmbaty 2938
 
2939
         current_offset = byte_index; // now reach the next segment
2940
      }
2941
   }
2942
 
7 pmbaty 2943
endofdata:
3 pmbaty 2944
   // at this point there's nothing left we're able to parse
2 pmbaty 2945
   if (current_offset < filesize)
3 pmbaty 2946
   {
2947
      printf ("End of identifiable data reached.\n");
7 pmbaty 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);
3 pmbaty 2949
   }
2 pmbaty 2950
 
3 pmbaty 2951
   printf ("End of file reached at offset 0x%zx (%zd)\n", filesize, filesize);
2952
   printf ("IFS dissecation complete.\n");
2 pmbaty 2953
   return (0);
3 pmbaty 2954
}