Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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