Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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

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