Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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

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