Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 15 | Rev 17 | 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
 
16 pmbaty 8
// standard C includes
1 pmbaty 9
#include <stdint.h>
10
#include <stdbool.h>
11
#include <stdlib.h>
2 pmbaty 12
#include <stdarg.h>
1 pmbaty 13
#include <stdio.h>
14
#include <string.h>
16 pmbaty 15
#include <limits.h>
1 pmbaty 16
#include <errno.h>
17
#include <sys/stat.h>
18
#include <ctype.h>
19
#include <time.h>
20
 
16 pmbaty 21
// platform-specific includes
1 pmbaty 22
#ifdef _MSC_VER
23
#include <io.h>
14 pmbaty 24
#include <direct.h>
25
#include <sys/utime.h>
1 pmbaty 26
#else // !_MSC_VER
27
#include <sys/param.h>
28
#include <unistd.h>
14 pmbaty 29
#include <utime.h>
1 pmbaty 30
#endif // _MSC_VER
31
 
16 pmbaty 32
// own includes
33
#include "buffer.h"
34
#include "sha512.h"
35
#include "elffile.h"
36
#include "ifsfile.h"
37
#include "utility.h"
1 pmbaty 38
 
4 pmbaty 39
 
16 pmbaty 40
// compiler-specific glue
41
#ifndef _MSC_VER
42
#define sscanf_s sscanf // WARNING: TRUE FOR THIS FILE ONLY!
43
#endif // !_MSC_VER
4 pmbaty 44
 
8 pmbaty 45
 
16 pmbaty 46
// libasan (Address Sanitizer) options: this is not a daemon, so I don't care about leaks: they will be recovered by the OS at program exit
47
const char *__asan_default_options () { return ("detect_leaks=0"); }
8 pmbaty 48
 
15 pmbaty 49
 
11 pmbaty 50
// placeholder value
51
#define WILL_BE_FILLED_LATER 0xbaadf00d // urgh
10 pmbaty 52
 
1 pmbaty 53
 
11 pmbaty 54
// miscellaneous macros
55
#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
56
#ifdef _WIN32
57
#define IS_DIRSEP(c) (((c) == '/') || ((c) == '\\')) // platform-specific directory separator, Win32 variant
16 pmbaty 58
#define PATH_SEP ";" // platform-specific PATH element separator (as string), Win32 variant
11 pmbaty 59
#else // !_WIN32, thus POSIX
60
#define IS_DIRSEP(c) ((c) == '/') // platform-specific directory separator, UNIX variant
16 pmbaty 61
#define PATH_SEP ":" // platform-specific PATH element separator (as string), UNIX variant
11 pmbaty 62
#endif // _WIN32
16 pmbaty 63
#define RECORD_SEP "\x1e" // arbitrarily-chosen ASCII record separator, as a C string suitable for e.g. strtok()
11 pmbaty 64
 
65
 
1 pmbaty 66
#define INITIAL_STARTUP_SCRIPT \
67
   /* procmgr_symlink /proc/boot/ldqnx-64.so.2 /usr/lib/ldqnx-64.so.2 */ \
7 pmbaty 68
   "\x34\x00" /*size*/ "\x04" /*type*/ "\x00" /*spare*/ "/proc/boot/ldqnx-64.so.2\0" "/usr/lib/ldqnx-64.so.2\0" \
1 pmbaty 69
   /* sh /proc/boot/startup.sh */ \
70
   "\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*/ \
71
   /* display_msg "Startup complete */ \
72
   "\x18\x00" /*size*/ "\x03" /*type*/ "\x00" /*spare*/ "Startup complete\n\0" "\x00\00" /*padding*/ \
73
   /* trailer */ \
74
   "\x00\x00\x00\x00"
75
 
76
 
11 pmbaty 77
// IFS directory entry insertion parameters structure type definition
1 pmbaty 78
typedef struct parms_s
79
{
80
   int dperms; // directory permissions (e.g. 0755)
81
   int perms; // file permissions (e.g. 0644)
82
   int uid; // owner user ID (e.g. 0 = root)
83
   int gid; // owner group ID (e.g. 0 = root)
84
   int st_mode; // entry type (e.g. S_IFREG for files) and permissions
7 pmbaty 85
   uint32_t mtime; // entry's modification time POSIX timestamp - set to UINT32_MAX to use the concerned files' mtime on the build host
86
   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 87
   char prefix[MAXPATHLEN]; // install path (e.g. "proc/boot")
7 pmbaty 88
   bool should_follow_symlinks; // follow symlinks
89
   bool should_autosymlink_dylib; // dynamic libraries should be written under their official SONAME and a named symlink be created pointing at them
15 pmbaty 90
   bool should_keep_ld_output; // whether to keep .sym files produced by ld calls, togglable by the [+keeplinked] attribute
2 pmbaty 91
   bool is_compiled_bootscript; // entry has [+script] attribute
10 pmbaty 92
   int extra_ino_flags; // bitmap of extra inode flags (IFS_INO_xxx)
1 pmbaty 93
   char search[10 * MAXPATHLEN]; // binary search path (the default one will be constructed at startup)
2 pmbaty 94
 
16 pmbaty 95
   buffer_t data; // the resolved file's own data bytes
1 pmbaty 96
} parms_t;
97
 
98
 
16 pmbaty 99
// exported globals
100
int verbose_level = 1; // verbosity level, can be increased with multiple -v[...] flags
101
 
102
 
103
// global variables used in this module only
1 pmbaty 104
static char line_buffer[4096]; // scrap buffer for the IFS build file parser
105
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
106
static uint32_t image_end = UINT32_MAX; // default image end (no limit)
107
static uint32_t image_maxsize = UINT32_MAX; // default image max size (no limit)
108
static uint32_t image_totalsize = 0; // image total size, measured once all the blocks have been written to the output IFS file
109
static uint32_t image_align = 4; // default image alignment, as per QNX docs
110
static uint32_t image_kernel_ino = 0;
111
static uint32_t image_bootscript_ino = 0;
7 pmbaty 112
#if defined(__x86_64__)
1 pmbaty 113
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 114
#elif defined(__aarch64__)
115
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)
116
#else // unknown platform
117
#error Please port ifstool to this platform
118
#endif
1 pmbaty 119
static char *buildfile_pathname = NULL; // pathname of IFS build file
7 pmbaty 120
static char *current_line = NULL; // copy of current line in IFS build file
1 pmbaty 121
static int lineno = 0; // current line number in IFS build file
122
static char *QNX_TARGET = NULL; // value of the $QNX_TARGET environment variable
7 pmbaty 123
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 124
 
15 pmbaty 125
// bootable IFS support
126
static char *bootfile_pathname = NULL;           // HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS
127
static size_t bootfile_size = 0;                 // HACK: size of the bootcode binary blob file to put at the start of a bootable IFS
128
static char *startupfile_pathname = NULL;        // HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS
129
static size_t startupfile_ep_from_imagebase = 0; // HACK: startup code entrypoint offset from image base for a bootable IFS 
130
static char *kernelfile_pathname = NULL;         // HACK: pathname to precompiled kernel file blob to put in a bootable IFS
131
static size_t kernelfile_offset = 0;             // HACK: kernel file offset in bootable IFS
1 pmbaty 132
 
15 pmbaty 133
 
16 pmbaty 134
// exported function prototypes
135
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
136
 
137
 
1 pmbaty 138
// prototypes of local functions
139
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)
16 pmbaty 140
static char *resolve_pathname (const char *pathname, const char *search_path); // locates pathname among MKIFS_PATH and returns a pointer to the resolved pathname (static string)
10 pmbaty 141
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
16 pmbaty 142
static size_t Buffer_WriteIFSDirectoryEntryAt (buffer_t *ifs_data, const size_t write_offset, const fsentry_t *fsentry); // writes the given filesystem entry (without its contents) to the IFS buffer
143
static size_t Buffer_AppendIFSFileData (buffer_t *ifs_data, fsentry_t *fsentry); // writes the given filesystem entry's file data (i.e. its contents) to the IFS buffer
144
static int Buffer_StripELFFile (buffer_t *file, const char *indicative_pathname); // strips an ELF file buffer the way mkifs does it and returns whether it succeeded
7 pmbaty 145
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 146
static int fsentry_compare_pathnames_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by pathnames
7 pmbaty 147
static void update_MKIFS_PATH (const char *processor);
1 pmbaty 148
 
149
 
16 pmbaty 150
// imported function prototypes
151
extern int dump_ifs_info (const char *ifs_pathname, bool want_everything); // [implemented in ifsdump.c] dumps detailed info about a particular IFS file on the standard output, returns 0 on success and >0 on error
152
extern int dump_ifs_contents (const char *ifs_pathname, const char *outdir); // [implemented in ifsdump.c] dumps the IFS filesystem contents in outdir, returns 0 on success and >0 on error
153
extern int dump_file_hex (const char *pathname); // [implemented in ifsdump.c] dumps the contents of pathname to stdout in mixed hexadecimal + ASCII (hex editor) format
1 pmbaty 154
 
155
 
16 pmbaty 156
int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness)
1 pmbaty 157
{
6 pmbaty 158
   // 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 159
 
5 pmbaty 160
   uint8_t accumulator[4] = { 0, 0, 0, 0 };
7 pmbaty 161
   const char *current_char_ptr;
162
   int32_t image_cksum;
5 pmbaty 163
   size_t i;
164
 
7 pmbaty 165
   image_cksum = 0;
166
   current_char_ptr = data;
5 pmbaty 167
   for (i = 0; i < data_len; i++)
168
   {
169
      accumulator[i % 4] = *current_char_ptr;
170
      if (i % 4 == 3)
171
         if (is_foreign_endianness)
8 pmbaty 172
            image_cksum += (accumulator[3] << 0) + (accumulator[2] << 8) + (accumulator[1] << 16) + (accumulator[0] << 24);
5 pmbaty 173
         else
8 pmbaty 174
            image_cksum += (accumulator[0] << 0) + (accumulator[1] << 8) + (accumulator[2] << 16) + (accumulator[3] << 24);
5 pmbaty 175
      current_char_ptr++;
176
   }
177
 
178
   return (is_foreign_endianness ? __builtin_bswap32 (-image_cksum) : -image_cksum);
179
}
180
 
181
 
1 pmbaty 182
static long long read_integer (const char *str)
183
{
184
   // 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)
185
 
186
   char *endptr = NULL;
187
   long long ret = strtoll (str, &endptr, 0); // use strtoll() to handle hexadecimal (0x...), octal (0...) and decimal (...) bases
188
   if (endptr != NULL)
189
   {
15 pmbaty 190
      if      ((*endptr == 'k') || (*endptr == 'K')) ret *= (size_t) 1024;
1 pmbaty 191
      else if ((*endptr == 'm') || (*endptr == 'M')) ret *= (size_t) 1024 * 1024;
192
      else if ((*endptr == 'g') || (*endptr == 'G')) ret *= (size_t) 1024 * 1024 * 1024;
193
      else if ((*endptr == 't') || (*endptr == 'T')) ret *= (size_t) 1024 * 1024 * 1024 * 1024; // future-proof enough, I suppose?
194
   }
195
   return (ret);
196
}
197
 
198
 
16 pmbaty 199
static char *resolve_pathname (const char *pathname, const char *search_path)
2 pmbaty 200
{
16 pmbaty 201
   // locates pathname among search path and returns resolved pathname (static buffer) or NULL.
2 pmbaty 202
 
16 pmbaty 203
   static thread_local char *resolved_pathname = NULL;
2 pmbaty 204
 
16 pmbaty 205
   struct stat stat_buf;
206
   const char *nextsep;
207
   const char *token;
2 pmbaty 208
 
16 pmbaty 209
   // initial allocation (per thread)
210
   if (resolved_pathname == NULL)
2 pmbaty 211
   {
16 pmbaty 212
      resolved_pathname = malloc (MAXPATHLEN);
213
      ASSERT_WITH_ERRNO (resolved_pathname);
2 pmbaty 214
   }
215
 
7 pmbaty 216
   // is it an absolute pathname (POSIX and Windows variants) ?
217
   if (IS_DIRSEP (pathname[0]) || (isalpha (pathname[0]) && (pathname[1] == ':') && IS_DIRSEP (pathname[2])))
16 pmbaty 218
      strcpy_s (resolved_pathname, MAXPATHLEN, pathname); // in this case, it MUST exist at its designated location (either absolute or relative to the current working directory)
7 pmbaty 219
   else // the path is relative, search it among the search paths we have
220
   {
221
      // construct a potential final path using each element of the search path
8 pmbaty 222
      token = (*search_path != 0 ? search_path : NULL);
16 pmbaty 223
      nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP)] : NULL);
7 pmbaty 224
      while (token != NULL)
225
      {
16 pmbaty 226
         sprintf_s (resolved_pathname, MAXPATHLEN, "%.*s/%s", (int) (nextsep - token), token, pathname);
227
         if ((stat (resolved_pathname, &stat_buf) == 0) && S_ISREG (stat_buf.st_mode))
228
            return (resolved_pathname); // if a file can indeed be found at this location, stop searching
7 pmbaty 229
 
230
         token = (*nextsep != 0 ? nextsep + 1 : NULL);
16 pmbaty 231
         nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP)] : NULL);
7 pmbaty 232
      }
233
   }
234
 
16 pmbaty 235
   errno = ENOENT; // we exhausted all possibilities
236
   return (NULL); // file not found, return with ENOENT
237
}
7 pmbaty 238
 
16 pmbaty 239
 
240
static size_t Buffer_WriteIFSDirectoryEntryAt (buffer_t *ifs, const size_t write_offset, const fsentry_t *fsentry)
241
{
242
   // writes a directory entry in the image filesystem buffer pointed to by ifs at write_offset (or fakes so if ifs is NULL)
243
   // and return the number of bytes written (or that would have been written)
244
 
245
   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";
246
 
247
   size_t datalen;
248
   size_t count;
249
 
250
   count = 0;
251
   if (ifs != NULL)
252
      ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->header, sizeof (fsentry->header))); // write the entry header (PACKED STRUCT)
253
   count += sizeof (fsentry->header);
254
   if (S_ISREG (fsentry->header.mode))
15 pmbaty 255
   {
16 pmbaty 256
      if (ifs != NULL)
257
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.file.offset, sizeof (uint32_t))); // write offset
258
      count += sizeof (uint32_t);
259
      if (ifs != NULL)
260
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.file.size,   sizeof (uint32_t))); // write size
261
      count += sizeof (uint32_t);
262
      datalen = strlen (fsentry->u.file.path) + 1;
263
      if (ifs != NULL)
264
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.file.path, datalen)); // write null-terminated path (no leading slash)
265
      count += datalen;
15 pmbaty 266
   }
16 pmbaty 267
   else if (S_ISDIR (fsentry->header.mode))
7 pmbaty 268
   {
16 pmbaty 269
      datalen = strlen (fsentry->u.dir.path) + 1;
270
      if (ifs != NULL)
271
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.dir.path, datalen)); // write null-terminated path (no leading slash)
272
      count += datalen;
7 pmbaty 273
   }
16 pmbaty 274
   else if (S_ISLNK (fsentry->header.mode))
7 pmbaty 275
   {
16 pmbaty 276
      if (ifs != NULL)
277
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.symlink.sym_offset, sizeof (uint16_t))); // write offset
278
      count += sizeof (uint16_t);
279
      if (ifs != NULL)
280
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.symlink.sym_size,   sizeof (uint16_t))); // write size
281
      count += sizeof (uint16_t);
282
      datalen = strlen (fsentry->u.symlink.path) + 1;
283
      if (ifs != NULL)
284
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.symlink.path, datalen)); // write null-terminated path (no leading slash)
285
      count += datalen;
286
      datalen = strlen (fsentry->u.symlink.contents) + 1;
287
      if (ifs != NULL)
288
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.symlink.contents, datalen)); // write null-terminated symlink contents
289
      count += datalen;
7 pmbaty 290
   }
16 pmbaty 291
   else
292
   {
293
      if (ifs != NULL)
294
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.device.dev,  sizeof (uint32_t))); // write dev number
295
      count += sizeof (uint32_t);
296
      if (ifs != NULL)
297
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.device.rdev, sizeof (uint32_t))); // write rdev number
298
      count += sizeof (uint32_t);
299
      datalen = strlen (fsentry->u.device.path) + 1;
300
      if (ifs != NULL)
301
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.device.path, datalen)); // write null-terminated path (no leading slash)
302
      count += datalen;
303
   }
7 pmbaty 304
 
16 pmbaty 305
   ASSERT (count <= fsentry->header.size, "attempt to write invalid dirent (claimed size %zd, written size %zd). Aborting.", (size_t) fsentry->header.size, count);
306
   if (count < fsentry->header.size)
307
   {
308
      if (ifs != NULL)
309
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, zeropad_buffer, fsentry->header.size - count)); // pad as necessary
310
      count += fsentry->header.size - count;
311
   }
312
 
313
   return (count);
7 pmbaty 314
}
315
 
316
 
16 pmbaty 317
static size_t Buffer_AppendIFSFileData (buffer_t *ifs_data, fsentry_t *fsentry)
1 pmbaty 318
{
16 pmbaty 319
   // writes the given filesystem entry's file data (i.e. its contents) to the IFS buffer
1 pmbaty 320
 
16 pmbaty 321
   elf_program_header_t *phdr;
322
   elf_header_t *elf;
323
   size_t corrective_offset;
324
   //size_t segment_type;
325
   size_t segment_size;
326
   size_t table_index;
327
   size_t table_count;
328
   size_t data_offset;
1 pmbaty 329
 
16 pmbaty 330
   ASSERT (S_ISREG (fsentry->header.mode), "function called for invalid dirent"); // consistency check
331
   data_offset = ifs_data->size; // see where we are
1 pmbaty 332
 
16 pmbaty 333
   // is the file we're storing a preprocessed ELF file ?
334
   if ((fsentry->header.ino & IFS_INO_PROCESSED_ELF)
335
#ifndef PROCNTO_WIP
336
      && (strstr (fsentry->u.file.path, "/procnto-smp-instr") == NULL)
337
#endif // !PROCNTO_WIP
338
       )
1 pmbaty 339
   {
16 pmbaty 340
 
341
      elf = (elf_header_t *) fsentry->u.file.UNSAVED_databuf; // quick access to ELF header
342
      table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers
343
      for (table_index = 0; table_index < table_count; table_index++)
344
      {
345
         phdr = (elf_program_header_t *) &fsentry->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
346
         //segment_type = ELF_GET_NUMERIC (elf, phdr, segment_type); // get segment type
347
         //if (!((segment_type >= 2) && (segment_type <= 7) || ((segment_type >= 0x6474e550) && (segment_type <= 0x6474e552)) || (segment_type == 0x70000001)))
348
         //   continue; // NOTE: only certain segments types must be corrected
349
 
350
 
351
         corrective_offset = ELF_GET_NUMERIC (elf, phdr, virtual_addr) - ELF_GET_NUMERIC (elf, phdr, file_offset);
352
         segment_size = ELF_GET_NUMERIC (elf, phdr, size_in_memory); // get this ELF segment's occupied size in memory
353
         if (segment_size != 0) // only patch the physical address of segments that have an actual size in memory
354
            ELF_SET_NUMERIC (elf, phdr, physical_addr, ELF_GET_NUMERIC (elf, phdr, physical_addr) + image_base + data_offset - corrective_offset); // patch the physical address member of the program header table (NOTE: data_offset is the location where the file data is about to be written)
355
      }
1 pmbaty 356
   }
357
 
16 pmbaty 358
   ASSERT_WITH_ERRNO (Buffer_Append (ifs_data, fsentry->u.file.UNSAVED_databuf, fsentry->u.file.size)); // write file data blob
359
   return (ifs_data->size - data_offset); // return the number of bytes written
1 pmbaty 360
}
361
 
362
 
16 pmbaty 363
static inline size_t Buffer_LocateOrAppendIfNecessaryAndReturnOffsetOf (buffer_t *buffer, const char *str)
10 pmbaty 364
{
16 pmbaty 365
   // helper function used in add_fsentry(): locates or appends str to buffer and returns its relative offset in the buffer
366
 
367
   size_t str_len_including_terminator = strlen (str) + 1;
368
   void *occurrence = Buffer_FindFirst (buffer, str, str_len_including_terminator);
369
   if (occurrence == NULL)
370
   {
371
      ASSERT_WITH_ERRNO (Buffer_Append (buffer, str, str_len_including_terminator));
372
      occurrence = Buffer_FindFirst (buffer, str, str_len_including_terminator);
373
      ASSERT_WITH_ERRNO (occurrence);
374
   }
375
   return (Buffer_OffsetOf (buffer, occurrence)); // can't fail
10 pmbaty 376
}
377
 
378
 
16 pmbaty 379
static int Buffer_StripELFFile (buffer_t *file, const char *indicative_pathname)
10 pmbaty 380
{
16 pmbaty 381
   // NOTE: for each ELF file, mkifs
382
   // -> 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)
383
   // -> 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
384
   // 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 385
 
16 pmbaty 386
   // reconstructed ELF:
387
   // ==== START OF FILE ====
388
   // ELF header
389
   // program header table
390
   //  (same sections, just p_addr offset changed)
391
   // section data 5 (named ".note.gnu.build-id")
392
   //  "............GNU....ZY.....c.o..l"
393
   // PROGRAM
394
   // sections table
395
   // + section 1: ALL ZEROES
396
   // + 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"
397
   // + 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"
398
   // + 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.."
399
   // + section 5: fileoffs 0x190 size 0x32 --> ".note.gnu.build-id" --> GNU build ID
400
   // + section 6: fileoffs 0x256e size 0x40 --> ".shstrtab" --> sections names strings table
401
   // section data 2 (named "QNX_info")
402
   //  (QNX binary description)
403
   // section data 3 (named ".gnu_debuglink")
404
   //  (debug file)
405
   // section data 4 (named "QNX_usage")
406
   //  (help text)
407
   // section data 6 (named ".shstrtab")
408
   //  "\0"
409
   //  ".shstrtab\0"
410
   //  "QNX_info\0"
411
   //  ".gnu_debuglink\0"
412
   //  "QNX_usage\0"
413
   //  ".note.gnu.build-id\0"
414
   // ==== END OF FILE ====
10 pmbaty 415
 
16 pmbaty 416
   #define ELFHDR ((elf_header_t *) file->bytes) // this convenient definition will make sure the ELF header points at the right location, even after entry_parms.data->byte is reallocated
417
   #define ADD_SECTION(section_name,section_ptr) do { \
418
      void *reallocated_ptr = realloc (elf_sections, (elf_section_count + 1) * sizeof (elf_section_t)); \
419
      ASSERT_WITH_ERRNO (reallocated_ptr); \
420
      elf_sections = reallocated_ptr; \
421
      elf_sections[elf_section_count].name = (section_name); \
422
      Buffer_Initialize (&elf_sections[elf_section_count].data); \
423
      *(section_ptr) = &elf_sections[elf_section_count]; \
424
      elf_section_count++; \
425
   } while (0)
426
 
427
   typedef struct elf_section_s
10 pmbaty 428
   {
16 pmbaty 429
      const char *name;
430
      elf_section_header_t header;
431
      buffer_t data;
432
   } elf_section_t;
10 pmbaty 433
 
16 pmbaty 434
   static const char *saved_sections[] = { "QNX_info", ".gnu_debuglink", "QNX_usage", ".note.gnu.build-id" };
10 pmbaty 435
 
16 pmbaty 436
   const elf_program_header_t *phdr;
437
   const elf_section_header_t *shdr;
438
   elf_section_t *elf_sections = NULL; // mallocated
439
   elf_section_t *elf_section = NULL;
440
   size_t elf_section_count = 0;
441
   size_t new_shdrtable_offset;
442
   size_t sectiondata_start;
443
   size_t sectiondata_size;
444
   size_t array_index;
445
   size_t table_index;
446
   size_t table_count;
447
   size_t page_size;
10 pmbaty 448
 
16 pmbaty 449
   // find out the platform page size
450
   if (ELF_GET_NUMERIC (ELFHDR, ELFHDR, instruction_set) == ELF_MACHINE_X86_64)
451
      page_size = 4 * 1024; // 4 kb pages on Intel processors
452
   else if (ELF_GET_NUMERIC (ELFHDR, ELFHDR, instruction_set) == ELF_MACHINE_AARCH64)
453
      page_size = 16 * 1024; // 16 kb pages on ARM64
454
   else
455
   {
456
      errno = ENOTSUP; // unsupported architecture: set errno to something meaningful
457
      return (0); // and return an error value
458
   }
7 pmbaty 459
 
16 pmbaty 460
   // parse the program header table, and measure the farthest offset known by this table where we'll write the reconstructed section headers table
1 pmbaty 461
 
16 pmbaty 462
   new_shdrtable_offset = 0;
463
   table_count = ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_len);
464
   for (table_index = 0; table_index < table_count; table_index++)
1 pmbaty 465
   {
16 pmbaty 466
      phdr = (elf_program_header_t *) &file->bytes[ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_item_size) * table_index]; // quick access to program header
467
      if (ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_file) > new_shdrtable_offset)
468
         new_shdrtable_offset = ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_file); // keep track of the farthest offset known by the program headers table
1 pmbaty 469
   }
16 pmbaty 470
   /*
471
   size_t new_shdrtable_offset_method2 = 0;
472
   for (table_index = 0; table_index < table_count; table_index++)
1 pmbaty 473
   {
16 pmbaty 474
      phdr = (elf_program_header_t *) &file->bytes[ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_offset) + (size_t) ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_item_size) * table_index]; // quick access to program header
475
      size_t segment_type = ELF_GET_NUMERIC (ELFHDR, phdr, segment_type); // get segment type
476
      if (!((segment_type >= 2) && (segment_type <= 7)))
477
         continue; // NOTE: only certain segments types must be corrected
478
      if (ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory) > new_shdrtable_offset_method2)
479
         new_shdrtable_offset_method2 = ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory);
1 pmbaty 480
   }
16 pmbaty 481
   if (new_shdrtable_offset_method2 > new_shdrtable_offset)
482
      LOG_DEBUG ("METHOD2: %llx > %llx", new_shdrtable_offset_method2, new_shdrtable_offset);*/
483
   //new_shdrtable_offset = ROUND_TO_UPPER_MULTIPLE (new_shdrtable_offset, page_size); // round to page size
484
 
485
   // re-create the section header table
486
   ADD_SECTION (".shstrtab", &elf_section); // the first section will be the section names strings table
487
   ASSERT_WITH_ERRNO (Buffer_InitWithByteArray (&elf_section->data, "\0")); // initialize an empty section headers strings table
488
   ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&elf_section->data, ".shstrtab\0")); // append ".shstrtab" *INCLUDING* its null terminator
489
 
490
   // go through the saved sections array and see if such an ELF section is present in the ELF file
491
   for (array_index = 0; array_index < sizeof (saved_sections) / sizeof (saved_sections[0]); array_index++)
492
      if ((shdr = elf_get_section_header_by_name (ELFHDR, saved_sections[array_index])) != NULL) // does this ELF have such a section ?
1 pmbaty 493
      {
16 pmbaty 494
         ADD_SECTION (saved_sections[array_index], &elf_section); // yes, so save it
495
         sectiondata_start = ELF_GET_NUMERIC (ELFHDR, shdr, file_offset); // identify section data start offset
496
         sectiondata_size = ELF_GET_NUMERIC (ELFHDR, shdr, size); // identify section data length
497
         if (sectiondata_start + sectiondata_size >= new_shdrtable_offset) // should this section be moved ?
498
            ASSERT_WITH_ERRNO (Buffer_InitWithData (&elf_section->data, &file->bytes[sectiondata_start], sectiondata_size)); // have a copy of this section's data
499
         else
500
            Buffer_Initialize (&elf_section->data); // this section is located before the place where we'll write the new section headers table, thus it doesn't need to be moved
501
         //LOG_DEBUG ("%s: section '%s' start 0x%llx len 0x%llx", indicative_pathname, saved_sections[array_index], (unsigned long long) sectiondata_start, (unsigned long long) sectiondata_size);
502
 
503
         // prepare this section's "fixed" header
504
         memcpy (&elf_section->header, shdr, ELF_STRUCT_SIZE (ELFHDR, shdr)); // have a copy of the old section header first
505
         ELF_SET_NUMERIC (ELFHDR, &elf_section->header, name_offset, Buffer_LocateOrAppendIfNecessaryAndReturnOffsetOf (&elf_sections[0].data, elf_section->name)); // make sure this section name is in the ELF sections section header strings table and update the relative offset of the section name
1 pmbaty 506
      }
16 pmbaty 507
 
508
   // jump over the new section headers table and write the saved sections data after the section headers table
509
   file->size = new_shdrtable_offset + (1 + elf_section_count) * ELF_STRUCT_SIZE (ELFHDR, &elf_sections[0].header); // start by truncating the ELF file: assume there are no sections beyond the section headers table until known otherwise
510
   for (table_index = 1; table_index < elf_section_count; table_index++)
1 pmbaty 511
   {
16 pmbaty 512
      elf_section = &elf_sections[table_index]; // quick access to ELF section about to be written
513
      if (elf_section->data.bytes != NULL) // was this section data backed up waiting to be relocated ?
1 pmbaty 514
      {
16 pmbaty 515
         ELF_SET_NUMERIC (ELFHDR, &elf_section->header, file_offset, file->size); // fix section offset
516
         Buffer_AppendBuffer (file, &elf_section->data); // append this section's data to the ELF file
1 pmbaty 517
      }
518
   }
16 pmbaty 519
   // write the section header strings table as the last section
520
   elf_section = &elf_sections[0]; // quick access to ELF section about to be written
521
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, name_offset, Buffer_LocateOrAppendIfNecessaryAndReturnOffsetOf (&elf_sections[0].data, elf_section->name)); // update the relative offset of the section name
522
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, type, ELF_SECTIONTYPE_STRINGTABLE); // section type (SHT_STRTAB)
523
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, flags, 0); // section flags (we could set SHF_STRINGS i.e. 0x20 here, but mkifs does not, so mimic that)
524
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, virtual_addr, 0); // this section does not need to be mapped
525
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, file_offset, file->size); // fix section offset
526
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, size, elf_sections[0].data.size); // section size
527
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, linked_index, 0); // this section is not linked to any other
528
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, info, 0); // this section has no additional info
529
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, alignment, 1); // this section is byte-aligned
530
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, entry_size, 0); // this section is not a table, so entry_size is zero
531
   Buffer_AppendBuffer (file, &elf_section->data); // append section headers strings table section data to ELF file
1 pmbaty 532
 
16 pmbaty 533
   // now write the section headers table
534
   memset (&file->bytes[new_shdrtable_offset], 0, ELF_STRUCT_SIZE (ELFHDR, &elf_sections[0].header)); // the first section header is always zerofilled
535
   for (table_index = 1; table_index < elf_section_count; table_index++)
536
      Buffer_WriteAt (file, new_shdrtable_offset + table_index * ELF_STRUCT_SIZE (ELFHDR, &elf_sections[table_index].header), &elf_sections[table_index].header, ELF_STRUCT_SIZE (ELFHDR, &elf_sections[table_index].header)); // write each section header
537
   Buffer_WriteAt (file, new_shdrtable_offset + table_index * ELF_STRUCT_SIZE (ELFHDR, &elf_sections[table_index].header), &elf_sections[0].header, ELF_STRUCT_SIZE (ELFHDR, &elf_sections[0].header)); // write the section header names section header last
1 pmbaty 538
 
16 pmbaty 539
   // and finally fix the ELF master header
540
   ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_table_offset, new_shdrtable_offset);
541
   ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_table_len, 1 + elf_section_count); // take in account that the first entry in the section headers table is empty 
542
   ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_names_idx, elf_section_count); // the section headers strings table is the last section
543
 
544
   // align size with page size (4096 on x86, 16k on ARM), zerofilling the extra space
545
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (file, ROUND_TO_UPPER_MULTIPLE (file->size, page_size)));
546
 
547
   // cleanup
548
   for (table_index = 0; table_index < elf_section_count; table_index++)
549
      Buffer_Forget (&elf_sections[table_index].data); // free all sections' backing buffers
550
 
551
   #undef ELFHDR // undefine the macro that used to always point to the ELF header at the beginning of the file
552
   return (1); // success
1 pmbaty 553
}
554
 
555
 
7 pmbaty 556
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 557
{
16 pmbaty 558
   static thread_local char *candidate_pathname = NULL;
1 pmbaty 559
   static int inode_count = 0; // will be preincremented each time this function is called
560
 
7 pmbaty 561
   const char *original_stored_pathname = NULL;
16 pmbaty 562
   buffer_t *shstrtab = NULL;
7 pmbaty 563
   const char *canonical_dylib_name;
564
   const char *dynamic_strings; // strings table of the ".dynamic" section
565
   const char *last_dirsep;
15 pmbaty 566
   char *global_envstring = NULL;
567
   size_t global_envstring_len = 0;
568
   char *startup_name = NULL;
569
   char *procnto_name = NULL;
7 pmbaty 570
   char *resolved_pathname;
1 pmbaty 571
   void *reallocated_ptr;
7 pmbaty 572
   void *old_data;
1 pmbaty 573
   struct stat stat_buf;
574
   fsentry_t *fsentry;
575
 
16 pmbaty 576
   // initial allocation (per thread)
577
   if (candidate_pathname == NULL)
578
   {
579
      candidate_pathname = malloc (MAXPATHLEN);
580
      ASSERT_WITH_ERRNO (candidate_pathname);
581
   }
582
 
1 pmbaty 583
   if (S_ISDIR (entry_parms->st_mode)) // are we storing a directory ?
584
   {
15 pmbaty 585
      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 586
   }
587
   else if (S_ISREG (entry_parms->st_mode)) // else are we storing a regular file ?
588
   {
7 pmbaty 589
      if (strcmp (stored_pathname, "/proc/boot/boot") == 0) // is it the kernel ?
1 pmbaty 590
      {
7 pmbaty 591
         // HACK: for now just consider the kernel as a binary blob
592
         // FIXME: reimplement properly
15 pmbaty 593
#ifdef PROCNTO_WIP // FIXME: segment corruption somewhere!
594
         char *linebit_start;
595
         char *content_line;
596
         char *write_ptr;
597
         char *token;
598
         char *value;
16 pmbaty 599
         char *ctx;
15 pmbaty 600
         bool is_quoted_context;
601
         bool was_string_split;
602
 
603
         // parse each line of contents
16 pmbaty 604
         ASSERT (entry_parms->data.len > 0, "kernel specification without inline contents");
605
         for (content_line = strtok_r (entry_parms->data.bytes, "\n", &ctx); content_line != NULL; content_line = strtok_r (NULL, "\n", ctx))
15 pmbaty 606
         {
607
            while (isspace (*content_line))
608
               content_line++; // skip leading spaces
609
            if ((*content_line == '#') || (*content_line == 0))
610
               continue; // skip comments and empty lines
611
 
612
            // format of a line: [attributes] [env assignation] [...] [executable] [arg] [...] [comment]
613
            // example: "[uid=0 gid=0 perms=0700] CONFIG_PATH=/proc/boot:/etc procnto-smp-instr -v -mr -d 0777 -u 0777"
614
            //LOG_DEBUG ("parsing line: %s", content_line);
615
 
616
            // does this line start with an attribute block ?
617
            if (*content_line == '[')
618
            {
619
               content_line++; // skip the leading square bracket
620
               linebit_start = content_line; // remember where it starts
621
               is_quoted_context = false; // reach the next unescaped closing square bracket that is not between quotes
622
               while ((*content_line != 0) && !((*content_line == ']') && (content_line[-1] != '\\') && !is_quoted_context))
623
               {
624
                  if (*content_line == '"')
625
                     is_quoted_context ^= true; // remember when we're between quotes
626
                  else if (!is_quoted_context && (*content_line == ' '))
16 pmbaty 627
                     *content_line = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
15 pmbaty 628
                  content_line++; // reach the next unescaped closing square bracket
629
               }
630
               if (*content_line != ']')
631
               {
632
                  LOG ("warning", 0, "syntax error in \"%s\" line %d: unterminated attributes block (skipping)", buildfile_pathname, lineno);
633
                  continue; // invalid attribute block, skip line
634
               }
635
               *content_line = 0; // end the attribute block so that it is a parsable C string
636
 
637
               // now parse the attribute tokens (NOTE: THE LIST OF ALLOWED ATTRIBUTES HERE IS NOT DOCUMENTED)
16 pmbaty 638
               token = strtok_r (linebit_start, RECORD_SEP, &ctx);
15 pmbaty 639
               while (token != NULL)
640
               {
641
                  #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
642
                  if      (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms->uid     = (int) read_integer (value); }
643
                  else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms->gid     = (int) read_integer (value); }
644
                  else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms->perms   = (int) read_integer (value); }
645
                  else if (strncmp (token, "prefix=",  7) == 0) { REACH_TOKEN_VALUE (); strcpy (entry_parms->prefix, (*value == '/' ? value + 1 : value)); } // skip possible leading slash in prefix
646
                  else if (strcmp (token, "+followlink") == 0) entry_parms->should_follow_symlinks = true;
647
                  else if (strcmp (token, "-followlink") == 0) entry_parms->should_follow_symlinks = false;
648
                  else if (strcmp (token, "+keeplinked") == 0) entry_parms->should_keep_ld_output = true;
649
                  else if (strcmp (token, "-keeplinked") == 0) entry_parms->should_keep_ld_output = false;
650
                  else LOG_WARNING ("unimplemented bootstrap executable attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, token);
651
                  #undef REACH_TOKEN_VALUE
16 pmbaty 652
                  token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
15 pmbaty 653
               }
654
 
655
               content_line++; // reach the next character
656
               while ((*content_line != 0) && isspace (*content_line))
657
                  content_line++; // skip leading spaces
658
            } // end of "this line starts with an attributes block"
659
 
660
            // there's data in this line. We expect an executable OR a variable name. Read it and unescape escaped characters
661
            while (*content_line != 0)
662
            {
663
               linebit_start = content_line; // remember the name starts here
664
               write_ptr = linebit_start;
665
               is_quoted_context = (*content_line == '"');
666
               if (is_quoted_context)
667
                  content_line++; // skip a possible initial quote in the name
668
               while ((*content_line != 0) && ((!is_quoted_context && (*content_line != '=') && !isspace (*content_line)) || (is_quoted_context && (*content_line == '"'))))
669
               {
670
                  if (*content_line == '\\')
671
                  {
672
                     content_line++;
673
                     *write_ptr++ = *content_line; // unescape characters that are escaped with '\'
674
                  }
675
                  else
676
                     *write_ptr++ = *content_line;
677
                  content_line++;
678
               }
679
 
680
               // we reached a closing quote, a space OR an equal sign
681
               if (*content_line == '=')
682
               {
683
                  // it's an environment variable assignation
684
                  *write_ptr++ = *content_line++; // skip the equal sign
685
                  is_quoted_context = (*content_line == '"');
686
                  if (is_quoted_context)
687
                     content_line++; // skip a possible initial quote in the value
688
                  while ((*content_line != 0) && ((!is_quoted_context && (*content_line != '=') && !isspace (*content_line)) || (is_quoted_context && (*content_line == '"'))))
689
                  {
690
                     if (*content_line == '\\')
691
                     {
692
                        content_line++;
693
                        *write_ptr++ = *content_line; // unescape characters that are escaped with '\'
694
                     }
695
                     else
696
                        *write_ptr++ = *content_line;
697
                     content_line++;
698
                  }
699
                  if (*write_ptr != 0)
700
                  {
701
                     *write_ptr = 0; // terminate the string if necessary
702
                     was_string_split = true;
703
                  }
704
                  else
705
                     was_string_split = false;
706
                  if (is_quoted_context && (*content_line == '"'))
707
                     content_line++; // skip a possible final quote
708
                  while ((*content_line != 0) && isspace (*content_line))
709
                     content_line++; // skip spaces
710
 
711
                  // now linebit_start is of the form "NAME=VALUE"
712
                  LOG_DEBUG ("assignation: [%s]", linebit_start);
713
 
714
                  // TODO: grow global_envstring
715
 
716
                  //reallocated_ptr = realloc (global_envstring, global_envstring_len + strlen ())
717
 
718
                  if (was_string_split)
719
                     *write_ptr = ' '; // restore string continuity for parsing to continue
720
                  while ((*content_line != 0) && isspace (*content_line))
721
                     content_line++; // skip spaces
722
               }
723
               else // it's either a closing quote or a space
724
               {
725
                  *write_ptr = 0; // terminate the string
726
                  if (is_quoted_context && (*content_line == '"'))
727
                     content_line++; // skip a possible final quote
728
 
729
                  LOG_DEBUG ("exe name: [%s]", linebit_start);
730
 
731
                  while ((*content_line != 0) && isspace (*content_line))
732
                     content_line++; // skip leading spaces
733
 
734
                  // it's an executable name. As per QNX docs, the first executable must be startup-*, the last executable must be procnto.
735
                  if (startup_name == NULL)
736
                     startup_name = strdup (linebit_start);
737
                  else
738
                  {
739
                     if (procnto_name != NULL)
740
                        free (procnto_name);
741
                     procnto_name = strdup (linebit_start);
742
                  }
743
 
744
                  if ((*content_line == '#') || (*content_line == 0))
745
                     break; // if we reach the end of the line, stop parsing
746
 
747
                  // what comes after now must be optional arguments
748
                  while ((*content_line != 0) && isspace (*content_line))
749
                     content_line++; // skip leading spaces
750
 
751
                  // FIXME: parse executable command-line arguments
752
 
753
                  break; // stop parsing once all the arguments have been read
754
               }
755
            }
756
         } // end of parsing
757
         free (entry_parms->data.bytes); // free the inline specification once it's parsed
758
         entry_parms->data.bytes = NULL;
759
         entry_parms->data.len = 0;
760
 
16 pmbaty 761
         ASSERT (startup_name && *startup_name, "the QNX startup executable (startup-*) is missing in this bootstrap inline specification");
762
         ASSERT (procnto_name && *procnto_name, "the QNX kernel (procnto-*) is missing in this bootstrap inline specification");
15 pmbaty 763
 
764
         // now we know which startup and procnto executables to use
765
         LOG_DEBUG ("Startup: %s", startup_name);
766
         LOG_DEBUG ("Kernel: %s", procnto_name);
767
 
768
         sprintf (candidate_pathname, "%s/%s", entry_parms->prefix, procnto_name); // fix the entry name
7 pmbaty 769
         stored_pathname = candidate_pathname;
15 pmbaty 770
         entry_parms->extra_ino_flags |= /*IFS_INO_PROCESSED_ELF | */IFS_INO_BOOTSTRAP_EXE; // procnto needs to have these flags stamped on the inode
771
         entry_parms->st_mode = S_IFREG | entry_parms->perms; // apply specified procnto permissions
10 pmbaty 772
         image_kernel_ino = entry_parms->extra_ino_flags | (inode_count + 1);
15 pmbaty 773
 
774
         static thread_local char linker_pathname[MAXPATHLEN] = "";
775
         static thread_local char linker_sysroot_arg[MAXPATHLEN] = "";
776
         static thread_local char linker_script_pathname_arg[MAXPATHLEN] = "";
777
         static thread_local char procnto_buildhost_pathname[MAXPATHLEN] = "";
778
         static thread_local char procnto_sym_filename[MAXPATHLEN] = "";
779
 
780
         // construct the arguments that are based on environment variables (infer QNX_HOST from QNX_TARGET)
781
#if defined(_WIN32)
782
         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
783
#elif defined(__linux__)
784
         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"));
785
#elif defined(__QNXNTO__)
786
         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"));
787
#else // wtf are you building this on?
788
#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.
789
#endif
16 pmbaty 790
         ASSERT (access (linker_pathname, 0) == 0, "host cross-linker for QNX8 \"%s\" not found", linker_pathname);
15 pmbaty 791
         sprintf (linker_sysroot_arg, "--sysroot=%s/%s/", QNX_TARGET, image_processor);
792
         sprintf (linker_script_pathname_arg, "-T%s/%s/lib/nto.link", QNX_TARGET, image_processor);
793
 
16 pmbaty 794
         resolved_pathname = resolve_pathname (procnto_name, (entry_parms->search[0] != 0 ? entry_parms->search : MKIFS_PATH)); // locate the procnto kernel location
795
         ASSERT (resolved_pathname, "QNX kernel \"%s\" not found in search path", procnto_name);
15 pmbaty 796
         strcpy (procnto_buildhost_pathname, resolved_pathname);
797
 
798
         sprintf (procnto_sym_filename, "%s.sym", procnto_name);
799
 
800
         const char *linker_argv[] = // construct the linker invokation argv
801
         {
802
            strrchr (linker_pathname, '/') + 1, // "${TARGET_TRIPLE}-ld"
803
            linker_sysroot_arg, // "--sysroot=${QNX_TARGET}/${TARGET_CPU}/"
804
            linker_script_pathname_arg, // "-T${QNX_TARGET}/${TARGET_CPU}/lib/nto.link"
805
            "--section-start",
806
            ".text=0xffff800000001000",
807
            "--no-relax",
808
            procnto_buildhost_pathname, // "${QNX_TARGET}/${TARGET_CPU}/boot/sys/procnto-smp-instr"
809
            "-o",
810
            procnto_sym_filename, // "procnto-smp-instr.sym"
811
            NULL
812
         };
813
         if (verbose_level > 2)
814
         {
815
            fprintf (stderr, "ifstool: calling:");
816
            for (table_index = 0; table_index < sizeof (linker_argv) / sizeof (linker_argv[0]) - 1; table_index++)
817
               fprintf (stderr, " '%s'", linker_argv[table_index]);
818
            fputc ('\n', stderr);
819
         }
820
         _spawnv (_P_WAIT, linker_pathname, linker_argv); // spawn the linker and produce a stripped procnto (wait for completion)
16 pmbaty 821
         if (!Buffer_ReadFromFile (&entry_parms->data, procnto_sym_filename)) // load the output file
15 pmbaty 822
            DIE_WITH_EXITCODE (1, "the host cross-linker failed to produce a readable stripped \"%s\" kernel: %s", procnto_sym_filename, strerror (errno));
823
         if (!entry_parms->should_keep_ld_output)
824
            unlink (procnto_sym_filename); // remove the linker output file if we want to
825
#else // !PROCNTO_WIP
826
         /* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK */
827
         /* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK */
828
         /* HACK */
16 pmbaty 829
         /* HACK */ sprintf_s (candidate_pathname, MAXPATHLEN, "%s/procnto-smp-instr", entry_parms->prefix); // HACK: fix the entry name
15 pmbaty 830
         /* HACK */ stored_pathname = candidate_pathname;
831
         /* HACK */ entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // procnto needs to have these flags stamped on the inode
832
         /* HACK */ entry_parms->st_mode = S_IFREG | 0700; // procnto requires 0700 permissions
833
         /* HACK */ image_kernel_ino = entry_parms->extra_ino_flags | (inode_count + 1);
834
         /* HACK */ free (entry_parms->data.bytes); // discard inline contents
16 pmbaty 835
         /* HACK */ Buffer_Initialize (&entry_parms->data);
836
         /* HACK */ if (!Buffer_ReadFromFile (&entry_parms->data, kernelfile_pathname)) // read kernel file as a precompiled binary blob
15 pmbaty 837
         /* HACK */ {
838
         /* HACK */    fprintf (stderr, "fatal error: unable to read precompiled kernel file \"%s\" specified in --kernelfile argument\n", kernelfile_pathname);
839
         /* HACK */    exit (1);
840
         /* HACK */ }
841
         /* HACK */
842
         /* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK */
843
         /* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK *//* HACK */
844
#endif // PROCNTO_WIP
7 pmbaty 845
      }
846
      else if (entry_parms->is_compiled_bootscript) // else is it a startup script ?
847
         image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
2 pmbaty 848
 
7 pmbaty 849
      // do we already know the data for this data blob ?
11 pmbaty 850
      if (entry_parms->data.bytes != NULL)
7 pmbaty 851
      {
15 pmbaty 852
         entry_parms->mtime = entry_parms->mtime_for_inline_files; // if so, set it a mtime equal to the mtime to use for inline files
16 pmbaty 853
         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.size);
7 pmbaty 854
      }
8 pmbaty 855
      else if (buildhost_pathname != NULL) // else was a source file pathname supplied ?
1 pmbaty 856
      {
16 pmbaty 857
         resolved_pathname = resolve_pathname (buildhost_pathname, (entry_parms->search[0] != 0 ? entry_parms->search : MKIFS_PATH)); // locate the file
7 pmbaty 858
         if (resolved_pathname == NULL)
15 pmbaty 859
            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));
16 pmbaty 860
         if (!Buffer_ReadFromFile (&entry_parms->data, resolved_pathname))
861
            DIE_WITH_EXITCODE (1, "filesystem entry \"%s\" specified in \"%s\" line %d can't be read: %s", buildhost_pathname, buildfile_pathname, lineno, strerror (errno));
862
         stat (resolved_pathname, &stat_buf); // can't fail, since we could read it
7 pmbaty 863
         if (entry_parms->mtime == UINT32_MAX)
864
            entry_parms->mtime = (uint32_t) stat_buf.st_mtime;
16 pmbaty 865
         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.size);
8 pmbaty 866
      }
1 pmbaty 867
 
9 pmbaty 868
      // is the file we're storing an ELF file ?
16 pmbaty 869
      #define ELFHDR ((elf_header_t *) entry_parms->data.bytes) // this convenient definition will make sure the ELF header points at the right location, even after entry_parms.data->byte is reallocated
870
      if ((entry_parms->data.size > 52) // file is big enough to contain an ELF header
871
          && (memcmp (ELF_GET_STRING (ELFHDR, ELFHDR, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic
7 pmbaty 872
      {
9 pmbaty 873
         // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ?
16 pmbaty 874
         if ((ELF_GET_NUMERIC (ELFHDR, ELFHDR, type) == ELF_TYPE_DYNAMICLIB) && entry_parms->should_autosymlink_dylib)
7 pmbaty 875
         {
9 pmbaty 876
            // locate the sections we need (the dynamic section and its strings table)
16 pmbaty 877
            const elf_section_header_t *shdr_dynamic = elf_get_section_header_by_name (ELFHDR, ".dynamic");
878
            const elf_section_header_t *shdr_dynstr = elf_get_section_header_by_name (ELFHDR, ".dynstr");
7 pmbaty 879
 
9 pmbaty 880
            // make sure we have both the dynamic section header and its own strings table header
881
            if ((shdr_dynamic != NULL) && (shdr_dynstr != NULL))
882
            {
16 pmbaty 883
               dynamic_strings = (char *) &entry_parms->data.bytes[ELF_GET_NUMERIC (ELFHDR, shdr_dynstr, file_offset)]; // quick access to dynamic sections strings table
7 pmbaty 884
 
9 pmbaty 885
               // walk through the dynamic section, look for the DT_SONAME entry
16 pmbaty 886
               canonical_dylib_name = NULL; // assume none until told otherwise
887
               for (elf_dynamic_section_entry_t *dynamic_entry = (elf_dynamic_section_entry_t *) &entry_parms->data.bytes[ELF_GET_NUMERIC (ELFHDR, shdr_dynamic, file_offset)];
888
                    (ELF_GET_NUMERIC (ELFHDR, dynamic_entry, tag) != ELF_DT_NULL);
889
                    dynamic_entry = (elf_dynamic_section_entry_t *) ((uint8_t *) dynamic_entry + ELF_STRUCT_SIZE (ELFHDR, dynamic_entry)))
890
                  if (ELF_GET_NUMERIC (ELFHDR, dynamic_entry, tag) == ELF_DT_SONAME)
9 pmbaty 891
                  {
16 pmbaty 892
                     canonical_dylib_name = dynamic_strings + ELF_GET_NUMERIC (ELFHDR, dynamic_entry, value);
9 pmbaty 893
                     break;
894
                  }
895
 
896
               // do we have it ?
897
               if ((canonical_dylib_name != NULL) && (canonical_dylib_name[0] != 0))
7 pmbaty 898
               {
16 pmbaty 899
                  sprintf_s (candidate_pathname, MAXPATHLEN, "%s/%s", entry_parms->prefix, canonical_dylib_name);
9 pmbaty 900
                  if (strcmp (candidate_pathname, stored_pathname) != 0) // claimed dylib name differs from passed name ?
901
                  {
902
                     original_stored_pathname = stored_pathname; // if so, remember to create a symlink here
903
                     stored_pathname = candidate_pathname;
904
                  }
7 pmbaty 905
               }
1 pmbaty 906
            }
9 pmbaty 907
         } // end if the file we're storing is a dylib
10 pmbaty 908
 
909
         // now strip this ELF file if necessary
910
         if (!(entry_parms->extra_ino_flags & IFS_INO_PROCESSED_ELF))
911
         {
16 pmbaty 912
            Buffer_StripELFFile (&entry_parms->data, stored_pathname); // strip the ELF file à la mkifs
10 pmbaty 913
            entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF; // mark this inode as a preprocessed ELF file
914
         } // end if the file is not yet a processed ELF
9 pmbaty 915
      } // end if the file we're storing is an ELF file
16 pmbaty 916
      #undef ELFHDR // undefine the macro that used to always point to the ELF header at the beginning of the file
1 pmbaty 917
   }
918
   else if (S_ISLNK (entry_parms->st_mode)) // else are we storing a symbolic link ?
15 pmbaty 919
      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 920
   else // we must be storing a FIFO
921
   {
11 pmbaty 922
      if (strchr (entry_parms->data.bytes, ':') == NULL)
15 pmbaty 923
         DIE_WITH_EXITCODE (1, "device entry \"%s\" malformed (no 'dev:rdev' pair)", stored_pathname);
924
      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 925
   }
926
 
7 pmbaty 927
   // grow filesystem entries array to hold one more slot
1 pmbaty 928
   reallocated_ptr = realloc (*fsentries, (*fsentry_count + 1) * sizeof (fsentry_t)); // attempt to reallocate
16 pmbaty 929
   ASSERT_WITH_ERRNO (reallocated_ptr);
1 pmbaty 930
   *fsentries = reallocated_ptr; // save reallocated pointer
931
   fsentry = &(*fsentries)[*fsentry_count]; // quick access to fs entry slot
932
   fsentry->header.extattr_offset = 0;
10 pmbaty 933
   fsentry->header.ino = entry_parms->extra_ino_flags | (++inode_count);
1 pmbaty 934
   fsentry->header.mode = entry_parms->st_mode;
935
   fsentry->header.gid = entry_parms->gid;
936
   fsentry->header.uid = entry_parms->uid;
7 pmbaty 937
   fsentry->header.mtime = (entry_parms->mtime == UINT32_MAX ? (uint32_t) time (NULL) : entry_parms->mtime);
1 pmbaty 938
   if (S_ISDIR (entry_parms->st_mode))
939
   {
7 pmbaty 940
      fsentry->u.dir.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
16 pmbaty 941
 
7 pmbaty 942
      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 943
      fsentry->UNSAVED_was_data_written = true; // no data to save
944
   }
945
   else if (S_ISREG (entry_parms->st_mode))
946
   {
947
      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
16 pmbaty 948
      fsentry->u.file.size = (uint32_t) entry_parms->data.size;
7 pmbaty 949
      fsentry->u.file.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
16 pmbaty 950
      fsentry->u.file.UNSAVED_databuf = malloc (entry_parms->data.size);
951
      ASSERT_WITH_ERRNO (fsentry->u.file.UNSAVED_databuf);
952
      memcpy (fsentry->u.file.UNSAVED_databuf, entry_parms->data.bytes, entry_parms->data.size);
953
 
7 pmbaty 954
      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 955
      fsentry->UNSAVED_was_data_written = false; // there *IS* data to save
956
   }
957
   else if (S_ISLNK (entry_parms->st_mode))
958
   {
7 pmbaty 959
      fsentry->u.symlink.sym_offset = (uint16_t) (strlen (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname) + 1);
16 pmbaty 960
      fsentry->u.symlink.sym_size = (uint16_t) entry_parms->data.size;
7 pmbaty 961
      fsentry->u.symlink.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
11 pmbaty 962
      fsentry->u.symlink.contents = strdup (entry_parms->data.bytes);
16 pmbaty 963
      ASSERT_WITH_ERRNO (fsentry->u.symlink.contents);
964
 
7 pmbaty 965
      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 966
      fsentry->UNSAVED_was_data_written = true; // no data to save
967
   }
7 pmbaty 968
   else // necessarily a device node
1 pmbaty 969
   {
11 pmbaty 970
      fsentry->u.device.dev  = strtol (entry_parms->data.bytes, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
971
      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 972
      fsentry->u.device.path = strdup (stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname);
16 pmbaty 973
 
7 pmbaty 974
      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 975
      fsentry->UNSAVED_was_data_written = true; // no data to save
976
   }
977
   (*fsentry_count)++;
7 pmbaty 978
 
979
   // should we also add a symlink to this entry ? (in case we stored a dylib file under its canonical name)
980
   if (original_stored_pathname != NULL)
981
   {
982
      entry_parms->is_compiled_bootscript = false;
983
      entry_parms->should_autosymlink_dylib = false;
984
      entry_parms->should_follow_symlinks = false;
985
      entry_parms->st_mode = S_IFLNK | 0777; // NOTE: mkifs stores symlink permissions as rwxrwxrwx !
10 pmbaty 986
      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 987
      last_dirsep = strrchr (stored_pathname, '/');
11 pmbaty 988
      old_data = entry_parms->data.bytes; // backup previous data pointer
989
      entry_parms->data.bytes = (uint8_t *) (last_dirsep == NULL ? stored_pathname : last_dirsep + 1); // store symlink target in dirent data
16 pmbaty 990
      entry_parms->data.size = strlen (entry_parms->data.bytes);
7 pmbaty 991
      add_fsentry (fsentries, fsentry_count, entry_parms, original_stored_pathname, NULL);
11 pmbaty 992
      entry_parms->data.bytes = old_data; // restore previous data pointer so that it can be freed normally
7 pmbaty 993
   }
994
 
1 pmbaty 995
   return (*fsentry_count);
996
}
997
 
998
 
999
static int fsentry_compare_pathnames_cb (const void *a, const void *b)
1000
{
1001
   // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname
1002
 
3 pmbaty 1003
   const fsentry_t *entry_a = (const fsentry_t *) a;
1004
   const fsentry_t *entry_b = (const fsentry_t *) b;
1 pmbaty 1005
   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)));
1006
   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)));
1007
   return (strcmp (pathname_a, pathname_b));
1008
}
1009
 
1010
 
7 pmbaty 1011
static void update_MKIFS_PATH (const char *processor)
1 pmbaty 1012
{
7 pmbaty 1013
   // updates the value of MKIFS_PATH according to the passed processor name string, unless an environment variable already defines it
1 pmbaty 1014
 
7 pmbaty 1015
   char processor_base[16];
1016
   size_t data_len;
1017
   char *envvar;
1018
   char *token;
1019
 
1020
   envvar = getenv ("MKIFS_PATH"); // look in the environment first, and construct a default one if not supplied
1021
   if (envvar != NULL)
1022
      MKIFS_PATH = envvar; // if envvar is present, set MKIFS_PATH to point to it
1023
   else // envvar not present
1024
   {
1025
      if (MKIFS_PATH != NULL)
1026
         free (MKIFS_PATH); // free any MKIFS_PATH that we constructed earlier
1027
 
16 pmbaty 1028
      strcpy_s (processor_base, sizeof (processor_base), processor); // construct PROCESSOR_BASE
7 pmbaty 1029
      token = strchr (processor_base, '-');
1030
      if (token != NULL)
1031
         *token = 0; // split anything from the first dash onwards
1032
      data_len = strlen (processor_base);
1033
      if ((data_len > 2) && ((processor_base[data_len - 2] == 'b') || (processor_base[data_len - 2] == 'l')) && (processor_base[data_len - 1] == 'e'))
1034
         processor_base[data_len - 2] = 0; // if it ends with "le" or "be", strip that too
1035
 
1036
      MKIFS_PATH = malloc (10 * MAXPATHLEN); // construct a default MKIFS_PATH now
16 pmbaty 1037
      ASSERT_WITH_ERRNO (MKIFS_PATH);
1038
      sprintf_s (MKIFS_PATH, 10 * MAXPATHLEN,
1039
                 "." PATH_SEP "%s/%s/sbin" PATH_SEP "%s/%s/usr/sbin" PATH_SEP "%s/%s/boot/sys" PATH_SEP "%s/%s/boot/sys" PATH_SEP "%s/%s/bin" PATH_SEP "%s/%s/usr/bin" PATH_SEP "%s/%s/lib" PATH_SEP "%s/%s/lib/dll" PATH_SEP "%s/%s/usr/lib", // use a platform-specific character as path separator
1040
                 QNX_TARGET, processor,
1041
                 QNX_TARGET, processor,
1042
                 QNX_TARGET, processor,
1043
                 QNX_TARGET, processor_base,
1044
                 QNX_TARGET, processor,
1045
                 QNX_TARGET, processor,
1046
                 QNX_TARGET, processor,
1047
                 QNX_TARGET, processor,
1048
                 QNX_TARGET, processor);
7 pmbaty 1049
   }
1050
 
1051
   return;
1 pmbaty 1052
}
1053
 
1054
 
1055
int main (int argc, char **argv)
1056
{
1057
   // program entrypoint
1058
 
16 pmbaty 1059
   typedef struct ifs_offsets_s
1060
   {
1061
      size_t startupheader;
1062
      size_t startuptrailer;
1063
      size_t imageheader;
1064
      size_t imagedir;
1065
      size_t imagetrailer;
1066
   } ifs_offsets_t;
1067
   typedef struct ifs_s
1068
   {
1069
      buffer_t data;
1070
      ifs_offsets_t offsets;
1071
      size_t final_size; // final size: not known (because not set) until everything has been written
1072
   } ifs_t;
1 pmbaty 1073
 
2 pmbaty 1074
   static startup_header_t startup_header = { 0 }; // output IFS's startup header
1075
   static startup_trailer_v2_t startup_trailer = { 0 }; // output IFS's startup trailer (version 2, with SHA-512 checksum and int32 checksum)
1076
   static image_header_t image_header = { 0 }; // output IFS's imagefs header
1077
   static image_trailer_v2_t image_trailer = { 0 }; // output IFS's imagefs trailer (version 2, with SHA-512 checksum and int32 checksum)
1078
   static fsentry_t *fsentries = NULL; // output IFS's filesystem entries
1079
   static size_t fsentry_count = 0; // number of entries in the IFS filesystem
7 pmbaty 1080
   static parms_t default_parms = { // default parameters for a filesystem entry
1081
      .dperms = 0755,
1082
      .perms = 0644,
1083
      .uid = 0,
1084
      .gid = 0,
1085
      .st_mode = S_IFREG,
1086
      .mtime = UINT32_MAX,
1087
      .mtime_for_inline_files = UINT32_MAX,
1088
      .prefix = "/proc/boot",
1089
      .should_follow_symlinks = true, // [+|-followlink]
1090
      .should_autosymlink_dylib = true, // [+|-autolink]
1091
      .is_compiled_bootscript = false, // [+|-script]
10 pmbaty 1092
      .extra_ino_flags = 0,
7 pmbaty 1093
      .search = "",
11 pmbaty 1094
      .data = { NULL, 0 }
7 pmbaty 1095
   };
2 pmbaty 1096
   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 1097
 
7 pmbaty 1098
   char path_on_buildhost[MAXPATHLEN] = "";
1099
   char path_in_ifs[MAXPATHLEN] = "";
1 pmbaty 1100
   char *ifs_pathname = NULL;
1101
   void *reallocated_ptr;
7 pmbaty 1102
   struct tm utc_time;
1 pmbaty 1103
   struct stat stat_buf;
1104
   size_t imgdir_size;
7 pmbaty 1105
   size_t available_space;
1106
   size_t allocated_size;
1 pmbaty 1107
   size_t fsentry_index;
7 pmbaty 1108
   size_t largest_index;
1109
   size_t largest_size;
1 pmbaty 1110
   size_t curr_offset;
16 pmbaty 1111
   ifs_t ifs = { 0 };
8 pmbaty 1112
   int32_t checksum;
16 pmbaty 1113
   char *first_pathname = NULL;
1114
   char *second_pathname = NULL;
7 pmbaty 1115
   char *specifiedpathname_start;
1 pmbaty 1116
   char *directiveblock_start;
1117
   char *write_ptr;
7 pmbaty 1118
   char *line_ptr;
1 pmbaty 1119
   char *token;
1120
   char *value;
1121
   char *sep;
16 pmbaty 1122
   char *ctx;
1 pmbaty 1123
   int arg_index;
1124
   bool is_quoted_context = false;
1125
   bool is_escaped_char = false;
11 pmbaty 1126
   bool should_discard_inline_contents = false;
1 pmbaty 1127
   bool want_info = false;
10 pmbaty 1128
   bool want_everything = false;
1 pmbaty 1129
   bool want_help = false;
14 pmbaty 1130
   bool want_dump = false;
16 pmbaty 1131
   bool want_strip = false;
15 pmbaty 1132
   bool want_hexdump = false;
5 pmbaty 1133
   bool is_foreign_endianness;
1 pmbaty 1134
   int string_len;
1135
   int read_char;
1136
   FILE *buildfile_fp;
1137
 
1138
   // parse arguments
1139
   for (arg_index = 1; arg_index < argc; arg_index++)
1140
   {
1141
      if ((strcmp (argv[arg_index], "--bootfile") == 0) && (arg_index + 1 < argc)) // --bootfile path/to/blob.bin
1142
         bootfile_pathname = argv[++arg_index];
1143
      else if ((strcmp (argv[arg_index], "--startupfile") == 0) && (arg_index + 1 < argc)) // --startupfile path/to/blob.bin@0x1030
1144
      {
1145
         sep = strchr (argv[++arg_index], '@');
1146
         if ((sep == NULL) || (sep[1] == 0))
15 pmbaty 1147
            DIE_WITH_EXITCODE (1, "the --startupfile arguments expects <pathname>@<entrypoint_from_image_base>");
1 pmbaty 1148
         *sep = 0;
1149
         startupfile_pathname = argv[arg_index];
1150
         startupfile_ep_from_imagebase = (size_t) read_integer (sep + 1);
1151
      }
1152
      else if ((strcmp (argv[arg_index], "--kernelfile") == 0) && (arg_index + 1 < argc)) // --kernelfile path/to/blob.bin@0x32000
1153
      {
1154
         sep = strchr (argv[++arg_index], '@');
1155
         if ((sep == NULL) || (sep[1] == 0))
15 pmbaty 1156
            DIE_WITH_EXITCODE (1, "the --kernelfile arguments expects <pathname>@<fileoffset>");
1 pmbaty 1157
         *sep = 0;
1158
         kernelfile_pathname = argv[arg_index];
1159
         kernelfile_offset = (size_t) read_integer (sep + 1);
1160
      }
7 pmbaty 1161
      else if (strcmp (argv[arg_index], "-n") == 0)
1162
         default_parms.mtime_for_inline_files = 0; // inline files should have a mtime set to zero
1163
      else if (strcmp (argv[arg_index], "-nn") == 0)
1164
      {
11 pmbaty 1165
         default_parms.mtime = 0; // *all* files should have a mtime set to zero
7 pmbaty 1166
         default_parms.mtime_for_inline_files = 0;
1167
      }
14 pmbaty 1168
      else if ((strcmp (argv[arg_index], "--outdir") == 0) && (arg_index + 1 < argc)) // --outdir path
16 pmbaty 1169
         second_pathname = argv[++arg_index];
1170
      else if ((strcmp (argv[arg_index], "--outfile") == 0) && (arg_index + 1 < argc)) // --outfile pathname
1171
         second_pathname = argv[++arg_index];
1 pmbaty 1172
      else if (strcmp (argv[arg_index], "--info") == 0)
1173
         want_info = true;
14 pmbaty 1174
      else if (strcmp (argv[arg_index], "--dump") == 0)
1175
         want_dump = true;
15 pmbaty 1176
      else if (strcmp (argv[arg_index], "--hexdump") == 0) // voluntarily undocumented
1177
         want_hexdump = true;
16 pmbaty 1178
      else if (strcmp (argv[arg_index], "--strip") == 0)
1179
         want_strip = true;
10 pmbaty 1180
      else if (strcmp (argv[arg_index], "--everything") == 0)
1181
         want_everything = true;
15 pmbaty 1182
      else if (strncmp (argv[arg_index], "-v", 2) == 0) // -v[....]
1183
         verbose_level += (int) strlen (argv[arg_index] + 1); // increase verbosity by the number of characters in this flag
1 pmbaty 1184
      else if ((strcmp (argv[arg_index], "-?") == 0) || (strcmp (argv[arg_index], "--help") == 0))
1185
         want_help = true;
16 pmbaty 1186
      else if (first_pathname == NULL)
1187
         first_pathname = argv[arg_index];
1188
      else if (second_pathname == NULL)
1189
         second_pathname = argv[arg_index];
1190
      else
1191
         DIE_WITH_EXITCODE (1, "unrecognized option: '%s'", argv[arg_index]);
1 pmbaty 1192
   }
1193
 
1194
   // do we not have enough information to run ?
16 pmbaty 1195
   if (want_help || (first_pathname == NULL) || (!want_info && !want_dump && !want_hexdump && !want_strip && (second_pathname == NULL)))
1 pmbaty 1196
   {
16 pmbaty 1197
      FILE *out = (want_help ? stdout : stderr); // select the right output channel
1198
      fprintf (out, "ifstool - QNX in-kernel filesystem creation utility by Pierre-Marie Baty <pm@pmbaty.com>\n");
1199
      fprintf (out, "          version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
4 pmbaty 1200
      if (!want_help)
16 pmbaty 1201
         fprintf (out, "error: missing parameters\n");
1202
      fprintf (out, "usage:\n");
1203
      fprintf (out, "    ifstool [--bootfile <pathname>] [--startupfile <pathname>@<EP_from_imgbase>] [--kernelfile <pathname>@<fileoffs>] [-n[n]] [-v[...]] <buildfile> <outfile>\n");
1204
      fprintf (out, "    ifstool --info [--everything] <ifs file>\n");
1205
      fprintf (out, "    ifstool --dump [--outdir <path>] <ifs file>\n");
1206
      fprintf (out, "    ifstool --strip [--outfile <pathname>] <ELF file>\n");
1207
      fprintf (out, "    ifstool --help\n");
1208
      fprintf (out, "NOTE: the compilation feature requires predigested boot, startup and kernel files produced by mkifs.\n");
1 pmbaty 1209
      exit (want_help ? 0 : 1);
1210
   }
1211
 
14 pmbaty 1212
   // do we want info about a particular IFS ? if so, dissecate it
2 pmbaty 1213
   if (want_info)
16 pmbaty 1214
      exit (dump_ifs_info (first_pathname, want_everything));
2 pmbaty 1215
 
14 pmbaty 1216
   // else do we want to dump its contents ? if so, do so
1217
   else if (want_dump)
16 pmbaty 1218
      exit (dump_ifs_contents (first_pathname, (second_pathname != NULL ? second_pathname : ".")));
14 pmbaty 1219
 
15 pmbaty 1220
   // else do we want to hex dump a file ? (this is voluntarily undocumented)
1221
   else if (want_hexdump)
16 pmbaty 1222
      exit (dump_file_hex (first_pathname));
1223
 
1224
   // else do we want to strip an ELF file ? if so, do so
1225
   else if (want_strip)
15 pmbaty 1226
   {
16 pmbaty 1227
      buffer_t file;
1228
      ASSERT (Buffer_ReadFromFile (&file, first_pathname), "can't open \"%s\" for reading: %s", first_pathname, strerror (errno));
1229
      ASSERT (Buffer_StripELFFile (&file, first_pathname), "error stripping \"%s\": %s", first_pathname, strerror (errno));
1230
      ASSERT_WITH_ERRNO (Buffer_WriteToFile (&file, (second_pathname != NULL ? second_pathname : "<stdout>")));
15 pmbaty 1231
      exit (0);
1232
   }
1233
 
16 pmbaty 1234
   // we want to CREATE an IFS file
1235
   buildfile_pathname = first_pathname; // assign the pathnames properly
1236
   ifs_pathname = second_pathname;
1237
 
1 pmbaty 1238
   // make sure we have ${QNX_TARGET} pointing somewhere
1239
   QNX_TARGET = getenv ("QNX_TARGET");
1240
   if (QNX_TARGET == NULL)
15 pmbaty 1241
      DIE_WITH_EXITCODE (1, "the QNX_TARGET environment variable is not set");
1 pmbaty 1242
   else if (access (QNX_TARGET, 0) != 0)
15 pmbaty 1243
      DIE_WITH_EXITCODE (1, "the QNX_TARGET environment variable doesn't point to an existing directory");
1 pmbaty 1244
 
7 pmbaty 1245
   // prepare a default MKIFS_PATH assuming the host processor
1246
   update_MKIFS_PATH (image_processor);
1247
 
1 pmbaty 1248
   // open build file
16 pmbaty 1249
   fopen_s (&buildfile_fp, buildfile_pathname, "rb");
1 pmbaty 1250
   if (buildfile_fp == NULL)
15 pmbaty 1251
      DIE_WITH_EXITCODE (1, "unable to open build file \"%s\" for reading: %s", buildfile_pathname, strerror (errno));
1 pmbaty 1252
 
1253
   // stack up filesystem entries
1254
   memcpy (&entry_parms, &default_parms, sizeof (default_parms));
1255
   entry_parms.st_mode = S_IFDIR | default_parms.dperms;
7 pmbaty 1256
   add_fsentry (&fsentries, &fsentry_count, &entry_parms, "", NULL); // add the root dir first
1 pmbaty 1257
 
11 pmbaty 1258
   // parse the IFS build file line per line
1 pmbaty 1259
   while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL)
1260
   {
7 pmbaty 1261
      if (current_line != NULL)
1262
         free (current_line);
1263
      current_line = strdup (line_buffer);
16 pmbaty 1264
      ASSERT_WITH_ERRNO (current_line);
1 pmbaty 1265
      lineno++; // keep track of current line number
1266
 
1267
      line_ptr = line_buffer;
1268
      while ((*line_ptr != 0) && isspace (*line_ptr))
1269
         line_ptr++; // skip leading spaces
1270
 
1271
      if ((*line_ptr == 0) || (*line_ptr == '#'))
1272
         continue; // skip empty or comment lines
1273
 
1274
      string_len = (int) strlen (line_buffer);
1275
      if ((string_len > 0) && (line_buffer[string_len - 1] == '\n'))
1276
         line_buffer[string_len - 1] = 0; // chop off newline for easier debug output
1277
 
1278
      // reset entry values
1279
      memcpy (&entry_parms, &default_parms, sizeof (default_parms));
7 pmbaty 1280
      path_in_ifs[0] = 0;
1281
      path_on_buildhost[0] = 0;
11 pmbaty 1282
      should_discard_inline_contents = false;
1 pmbaty 1283
 
1284
      // does this line start with an attribute block ?
1285
      if (*line_ptr == '[')
1286
      {
1287
         line_ptr++; // skip the leading square bracket
1288
         directiveblock_start = line_ptr; // remember where it starts
1289
         is_quoted_context = false;
15 pmbaty 1290
         while ((*line_ptr != 0) && !((*line_ptr == ']') && (line_ptr[-1] != '\\') && !is_quoted_context))
1 pmbaty 1291
         {
1292
            if (*line_ptr == '"')
1293
               is_quoted_context ^= true; // remember when we're between quotes
1294
            else if (!is_quoted_context && (*line_ptr == ' '))
16 pmbaty 1295
               *line_ptr = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
1 pmbaty 1296
            line_ptr++; // reach the next unescaped closing square bracket
1297
         }
1298
         if (*line_ptr != ']')
1299
         {
15 pmbaty 1300
            LOG ("warning", 0, "syntax error in \"%s\" line %d: unterminated attributes block (skipping)", buildfile_pathname, lineno);
1 pmbaty 1301
            continue; // invalid attribute block, skip line
1302
         }
1303
         *line_ptr = 0; // end the attribute block so that it is a parsable C string
1304
 
1305
         // now parse the attribute tokens
1306
         // DOCUMENTATION: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/m/mkifs.html#mkifs__description
16 pmbaty 1307
         token = strtok_r (directiveblock_start, RECORD_SEP, &ctx);
1 pmbaty 1308
         while (token != NULL)
1309
         {
1310
            // evaluate attribute token
1311
            #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
1312
            if      (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.uid     = (int) read_integer (value); }
1313
            else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.gid     = (int) read_integer (value); }
1314
            else if (strncmp (token, "dperms=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms.dperms  = (int) read_integer (value); }
1315
            else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms.perms   = (int) read_integer (value); }
15 pmbaty 1316
            else if (strncmp (token, "type=",    5) == 0) { REACH_TOKEN_VALUE ();
1317
               if      (strcmp (value, "dir")  == 0) entry_parms.st_mode = S_IFDIR;
1318
               else if (strcmp (value, "file") == 0) entry_parms.st_mode = S_IFREG;
1319
               else if (strcmp (value, "link") == 0) entry_parms.st_mode = S_IFLNK;
1320
               else if (strcmp (value, "fifo") == 0) entry_parms.st_mode = S_IFIFO;
1321
               else DIE_WITH_EXITCODE (1, "invalid 'type' attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, value);
1322
            }
16 pmbaty 1323
            else if (strncmp (token, "prefix=",  7) == 0) { REACH_TOKEN_VALUE (); strcpy_s (entry_parms.prefix, sizeof (entry_parms.prefix), (*value == '/' ? value + 1 : value)); } // skip possible leading slash in prefix
1 pmbaty 1324
            else if (strncmp (token, "image=",   6) == 0) { REACH_TOKEN_VALUE ();
1325
               image_base = (uint32_t) read_integer (value); // read image base address
12 pmbaty 1326
               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 1327
               if ((sep = strchr (value, ',')) != NULL) image_maxsize   = (uint32_t) read_integer (sep + 1); // if we have a comma, read optional image max size
1328
               if ((sep = strchr (value, '=')) != NULL) image_totalsize = (uint32_t) read_integer (sep + 1); // if we have an equal sign, read optional image padding size
1329
               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 1330
               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 1331
            }
1332
            else if (strncmp (token, "virtual=", 8) == 0) { REACH_TOKEN_VALUE ();
1333
               if ((bootfile_pathname == NULL) || (startupfile_pathname == NULL) || (kernelfile_pathname == NULL)) // HACK until I figure out how to re-create them
15 pmbaty 1334
                  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 1335
               if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ?
1336
               {
1337
                  *sep = 0;
16 pmbaty 1338
                  strcpy_s (image_processor, sizeof (image_processor), value); // save processor
7 pmbaty 1339
                  update_MKIFS_PATH (image_processor);
1 pmbaty 1340
                  value = sep + 1;
1341
               }
12 pmbaty 1342
               //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 1343
               //strcpy (image_bootfile, bootfile_pathname); // FIXME: HACK
1344
               if (stat (bootfile_pathname, &stat_buf) != 0)
15 pmbaty 1345
                  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 1346
               bootfile_size = stat_buf.st_size; // save preboot file size
15 pmbaty 1347
               LOG_INFO ("processor \"%s\" bootfile \"%s\"\n", image_processor, bootfile_pathname);
11 pmbaty 1348
#if 1
1349
               // ######################################################################################################################################################################################################################################
1350
               // # FIXME: figure out how to re-create it: linker call involved
1351
               // # $ 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
1352
               // ######################################################################################################################################################################################################################################
16 pmbaty 1353
//               if (!Buffer_ReadFromFile (&entry_parms.data, kernelfile_pathname))
15 pmbaty 1354
//                  DIE_WITH_EXITCODE (1, "unable to read precompiled kernel file \"%s\" specified in --kernelfile argument: %s", kernelfile_pathname, strerror (errno));
11 pmbaty 1355
#else // nonworking
1356
               strcpy (path_on_buildhost, "procnto-smp-instr");
1357
#endif // nonworking
1 pmbaty 1358
            }
7 pmbaty 1359
            else if (strncmp (token, "mtime=", 6) == 0) { REACH_TOKEN_VALUE (); if (strcmp (value, "*") == 0) entry_parms.mtime = UINT32_MAX; else {
1360
                  // value *must* be "YYYY-MM-DD-HH:MM:SS" by specification
1361
                  memset (&utc_time, 0, sizeof (utc_time));
16 pmbaty 1362
                  if (sscanf_s (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)
7 pmbaty 1363
                  {
15 pmbaty 1364
                     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 1365
                     continue; // invalid attribute block, skip line
1366
                  }
1367
                  utc_time.tm_mon--; // convert month from [1-12] to [0-11]
1368
                  entry_parms.mtime = (uint32_t) mktime (&utc_time);
1369
               }
1370
            }
1371
            else if (strcmp (token, "+script")     == 0) {
2 pmbaty 1372
               entry_parms.is_compiled_bootscript = true;
16 pmbaty 1373
               ASSERT_WITH_ERRNO (Buffer_InitWithByteArray (&entry_parms.data, INITIAL_STARTUP_SCRIPT)); // FIXME: HACK until the script compiler is implemented
11 pmbaty 1374
               should_discard_inline_contents = true; // remember we already have data (so as to discard the inline block's contents)
2 pmbaty 1375
            }
7 pmbaty 1376
            else if (strcmp (token, "-script")     == 0) entry_parms.is_compiled_bootscript = false;
1377
            else if (strcmp (token, "+followlink") == 0) entry_parms.should_follow_symlinks = true;
1378
            else if (strcmp (token, "-followlink") == 0) entry_parms.should_follow_symlinks = false;
1379
            else if (strcmp (token, "+autolink")   == 0) entry_parms.should_autosymlink_dylib = true;
1380
            else if (strcmp (token, "-autolink")   == 0) entry_parms.should_autosymlink_dylib = false;
15 pmbaty 1381
            else if (strcmp (token, "+keeplinked") == 0) entry_parms.should_keep_ld_output = true;
1382
            else if (strcmp (token, "-keeplinked") == 0) entry_parms.should_keep_ld_output = false;
1383
            else LOG_WARNING ("unimplemented attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, token);
1 pmbaty 1384
            #undef REACH_TOKEN_VALUE
1385
 
16 pmbaty 1386
            token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
1 pmbaty 1387
         }
1388
 
1389
         line_ptr++; // reach the next character
1390
         while ((*line_ptr != 0) && isspace (*line_ptr))
1391
            line_ptr++; // skip leading spaces
1392
 
1393
         // are we at the end of the line ? if so, it means the attribute values that are set should become the default
1394
         if ((*line_ptr == 0) || (*line_ptr == '#'))
1395
         {
1396
            #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (entry_parms.attr != default_parms.attr) { \
15 pmbaty 1397
                  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 1398
                  default_parms.attr = entry_parms.attr; \
1399
               } } while (0)
1400
            #define APPLY_DEFAULT_ATTR_STR(attr,descr,fmt) do { if (strcmp (entry_parms.attr, default_parms.attr) != 0) { \
15 pmbaty 1401
                  LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d", default_parms.attr, entry_parms.attr, buildfile_pathname, lineno); \
16 pmbaty 1402
                  strcpy_s (default_parms.attr, sizeof (default_parms.attr), entry_parms.attr); \
1 pmbaty 1403
               } } while (0)
7 pmbaty 1404
            APPLY_DEFAULT_ATTR_NUM (dperms,                   "directory permissions",           "0%o");
1405
            APPLY_DEFAULT_ATTR_NUM (perms,                    "file permissions",                "0%o");
1406
            APPLY_DEFAULT_ATTR_NUM (uid,                      "owner ID",                        "%d");
1407
            APPLY_DEFAULT_ATTR_NUM (gid,                      "group ID",                        "%d");
1408
            APPLY_DEFAULT_ATTR_NUM (st_mode,                  "inode type",                      "0%o");
1409
            APPLY_DEFAULT_ATTR_STR (prefix,                   "prefix",                          "\"%s\"");
1410
            APPLY_DEFAULT_ATTR_NUM (is_compiled_bootscript,   "compiled script state",           "%d");
1411
            APPLY_DEFAULT_ATTR_NUM (should_follow_symlinks,   "symlink resolution",              "%d");
1412
            APPLY_DEFAULT_ATTR_NUM (should_autosymlink_dylib, "dylib canonical name symlinking", "%d");
15 pmbaty 1413
            APPLY_DEFAULT_ATTR_NUM (should_keep_ld_output,    "linker output preservation",      "%d");
1 pmbaty 1414
            #undef APPLY_DEFAULT_ATTR_STR
1415
            #undef APPLY_DEFAULT_ATTR_NUM
1416
            continue; // end of line reached, proceed to the next line
1417
         }
1418
         // end of attributes parsing
1419
      } // end of "this line starts with an attributes block"
1420
 
1421
      // there's data in this line. We expect a filename in the IFS. Read it and unescape escaped characters
16 pmbaty 1422
      string_len = sprintf_s (path_in_ifs, sizeof (path_in_ifs), "%s", entry_parms.prefix);
1 pmbaty 1423
      while ((string_len > 0) && (path_in_ifs[string_len - 1] == '/'))
1424
         string_len--; // chop off any trailing slashes from prefix
1425
      write_ptr = &path_in_ifs[string_len];
1426
      *write_ptr++ = '/'; // add ONE trailing slash
7 pmbaty 1427
      specifiedpathname_start = write_ptr; // remember the specified pathname will start here
1428
      is_quoted_context = (*line_ptr == '"');
1429
      if (is_quoted_context)
1430
         line_ptr++; // skip a possible initial quote
1 pmbaty 1431
      if (*line_ptr == '/')
1432
      {
15 pmbaty 1433
         LOG_WARNING ("paths in the IFS file should not begin with a leading '/' in \"%s\" line %d", buildfile_pathname, lineno);
1 pmbaty 1434
         line_ptr++; // consistency check: paths in the IFS should not begin with a '/'
1435
      }
7 pmbaty 1436
      while ((*line_ptr != 0) && ((!is_quoted_context && (*line_ptr != '=') && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"'))))
1 pmbaty 1437
      {
1438
         if (*line_ptr == '\\')
1439
         {
1440
            line_ptr++;
1441
            *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
1442
         }
1443
         else
1444
            *write_ptr++ = *line_ptr;
1445
         line_ptr++;
1446
      }
1447
      *write_ptr = 0; // terminate the string
7 pmbaty 1448
      if (is_quoted_context && (*line_ptr == '"'))
1449
         line_ptr++; // skip a possible final quote
1 pmbaty 1450
 
1451
      // we reached a space OR an equal sign
1452
      while ((*line_ptr != 0) && isspace (*line_ptr))
1453
         line_ptr++; // skip optional spaces after the filename in the IFS
1454
 
1455
      // do we have an equal sign ?
1456
      if (*line_ptr == '=') // we must be creating either a directory or a file, do we have an equal sign ?
1457
      {
1458
         line_ptr++; // skip the equal sign
1459
         while ((*line_ptr != 0) && isspace (*line_ptr))
1460
            line_ptr++; // skip optional spaces after the equal sign
1461
 
1462
         if (*line_ptr == 0)
1463
         {
15 pmbaty 1464
            LOG_WARNING ("syntax error in \"%s\" line %d: missing data specification after equal sign (skipping)", buildfile_pathname, lineno);
1 pmbaty 1465
            continue; // invalid symlink specification, skip line
1466
         }
1467
 
1468
         // read the host system's path, it may be either a path or a contents definition. Is it a content definition ?
1469
         if (*line_ptr == '{')
1470
         {
7 pmbaty 1471
            allocated_size = 0;
1472
 
1 pmbaty 1473
            line_ptr++; // skip the leading content definition
1474
            is_escaped_char = false;
1475
            for (;;)
1476
            {
1477
               read_char = fgetc (buildfile_fp);
1478
               if (read_char == EOF)
15 pmbaty 1479
                  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 1480
               else if ((read_char == '\\') && !is_escaped_char)
1 pmbaty 1481
                  is_escaped_char = true; // remember the next char is escaped
1482
               else if ((read_char == '}') && !is_escaped_char)
1483
                  break; // found an unescaped closing bracked, stop parsing
1484
               else
1485
               {
1486
                  is_escaped_char = false; // any other char, meaning the next one will not be escaped
11 pmbaty 1487
                  if (!should_discard_inline_contents) // only store the contents if we do NOT know the data yet
1 pmbaty 1488
                  {
16 pmbaty 1489
                     if (entry_parms.data.size == allocated_size) // reallocate in 4 kb blocks
1 pmbaty 1490
                     {
11 pmbaty 1491
                        reallocated_ptr = realloc (entry_parms.data.bytes, allocated_size + 4096);
16 pmbaty 1492
                        ASSERT_WITH_ERRNO (reallocated_ptr);
11 pmbaty 1493
                        entry_parms.data.bytes = reallocated_ptr;
7 pmbaty 1494
                        allocated_size += 4096;
1 pmbaty 1495
                     }
16 pmbaty 1496
                     entry_parms.data.bytes[entry_parms.data.size++] = read_char;
1 pmbaty 1497
                  }
1498
                  if (read_char == '\n')
8 pmbaty 1499
                     lineno++; // update line counter as we parse the inline content
1 pmbaty 1500
               }
8 pmbaty 1501
            } // end for
1 pmbaty 1502
         }
7 pmbaty 1503
         else // not a content definition between { brackets }, must be either a pathname on the build host, or the target of a symlink
1 pmbaty 1504
         {
7 pmbaty 1505
            is_quoted_context = (*line_ptr == '"');
1506
            if (is_quoted_context)
1507
               line_ptr++; // skip a possible initial quote
1508
            specifiedpathname_start = line_ptr; // remember where the specified pathname starts
1509
            write_ptr = line_ptr; // now unescape all characters
1510
            while ((*line_ptr != 0) && ((!is_quoted_context && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"'))))
1511
            {
1512
               if (*line_ptr == '\\')
1513
               {
1514
                  line_ptr++;
1515
                  *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
1516
               }
1517
               else
1518
                  *write_ptr++ = *line_ptr;
1519
               line_ptr++;
1520
            }
1521
            *write_ptr = 0; // terminate the string
1522
            if (is_quoted_context && (*line_ptr == '"'))
1523
               line_ptr++; // skip a possible final quote
1524
 
1525
            if (S_ISLNK (entry_parms.st_mode)) // are we storing a symlink ?
16 pmbaty 1526
               ASSERT_WITH_ERRNO (Buffer_InitWithCString (&entry_parms.data, specifiedpathname_start)); // if so, store the symlink target as the dirent's blob data
7 pmbaty 1527
            else // it's a build host filesystem path
16 pmbaty 1528
               strcpy_s (path_on_buildhost, sizeof (path_on_buildhost), specifiedpathname_start); // the path on the build host is given after the equal sign
1 pmbaty 1529
         }
1530
      }
1531
      else // no equal sign, meaning the file will have the same name on the build host filesystem
1532
      {
1533
         // consistency check: symlinks MUST have an equal sign
1534
         if (entry_parms.st_mode == S_IFLNK)
1535
         {
15 pmbaty 1536
            LOG_WARNING ("syntax error in \"%s\" line %d: missing equal sign and symlink target (skipping)", buildfile_pathname, lineno);
1 pmbaty 1537
            continue; // invalid symlink specification, skip line
1538
         }
1539
 
16 pmbaty 1540
         strcpy_s (path_on_buildhost, sizeof (path_on_buildhost), specifiedpathname_start); // the path on the build host is the one specified
7 pmbaty 1541
         sep = strrchr (specifiedpathname_start, '/');
1542
         if (sep != NULL)
1543
            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 1544
      }
1545
 
1546
      // now add this entry to the image filesystem
7 pmbaty 1547
      if (S_ISDIR (entry_parms.st_mode))
1548
         entry_parms.st_mode |= entry_parms.dperms;
1549
      else if (S_ISLNK (entry_parms.st_mode))
1550
         entry_parms.st_mode |= 0777; // NOTE: mkifs sets symlink permissions to rwxrwxrwx !?
1551
      else // file or device node
1552
         entry_parms.st_mode |= entry_parms.perms;
1 pmbaty 1553
 
7 pmbaty 1554
      add_fsentry (&fsentries, &fsentry_count, &entry_parms, path_in_ifs, path_on_buildhost); // and add filesystem entry
1 pmbaty 1555
 
11 pmbaty 1556
      if (entry_parms.data.bytes != NULL)
1557
         free (entry_parms.data.bytes); // if blob data was allocated, free it
1 pmbaty 1558
   }
1559
 
16 pmbaty 1560
   fclose (buildfile_fp); // finished parsing the build file
1 pmbaty 1561
 
16 pmbaty 1562
   //////////////////////////////////
1563
   // start constructing the IFS file
1564
 
1565
   Buffer_Initialize (&ifs.data);
1566
 
2 pmbaty 1567
   // do we have a startup file ? if so, this is a bootable image
1568
   if (startupfile_pathname != NULL)
1 pmbaty 1569
   {
1570
      // write boot prefix
11 pmbaty 1571
      // ######################################################################################################################################################################################################################################
1572
      // # FIXME: figure out how to re-create it
1573
      // ######################################################################################################################################################################################################################################
16 pmbaty 1574
      buffer_t file;
1575
      if (!Buffer_ReadFromFile (&file, bootfile_pathname))
1576
         DIE_WITH_EXITCODE (1, "failed to open \"%s\" for reading: %s", bootfile_pathname, strerror (errno));
1577
      ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&ifs.data, &file)); // write boot blob
1578
      Buffer_Forget (&file);
1579
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1580
 
16 pmbaty 1581
      ifs.offsets.startupheader = ifs.data.size; // save startup header offset for future use
1 pmbaty 1582
      memset (&startup_header, 0, sizeof (startup_header)); // prepare startup header
1583
      memcpy (startup_header.signature, "\xeb\x7e\xff\x00", 4); // startup header signature, i.e. 0xff7eeb
1584
      startup_header.version       = 1;
1585
      startup_header.flags1        = STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2; // flags, 0x21 (STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2)
1586
      startup_header.header_size   = sizeof (startup_header); // 256
1587
      if (strcmp (image_processor, "x86_64") == 0)
10 pmbaty 1588
         startup_header.machine = ELF_MACHINE_X86_64; // EM_X86_64
1 pmbaty 1589
      else if (strcmp (image_processor, "aarch64le") == 0)
10 pmbaty 1590
         startup_header.machine = ELF_MACHINE_AARCH64; // EM_AARCH64
1 pmbaty 1591
      else
15 pmbaty 1592
         DIE_WITH_EXITCODE (1, "unsupported processor type '%s' found in build file \"%s\"", image_processor, buildfile_pathname); // should not happen
1 pmbaty 1593
      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 1594
      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 1595
      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)
1596
      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)
1597
      startup_header.startup_size  = WILL_BE_FILLED_LATER;                                  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
2 pmbaty 1598
      startup_header.stored_size   = WILL_BE_FILLED_LATER;                                  // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
1 pmbaty 1599
      startup_header.imagefs_size  = WILL_BE_FILLED_LATER;                                  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
2 pmbaty 1600
      startup_header.preboot_size  = (uint16_t) bootfile_size;                              // [I ] Size of loaded before header, here 0xf30 or 3888 bytes (size of "bios.boot" file))
16 pmbaty 1601
      ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &startup_header, sizeof (startup_header))); // write startup header
1602
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1603
 
1604
      // ######################################################################################################################################################################################################################################
11 pmbaty 1605
      // # FIXME: figure out how to re-create it:
1606
      // first: open "startup-x86" ELF file,
1607
      //        lookup section headers table (there is no program headers table in this one)
1608
      //        FIXME: figure out something in there where the result is 0x1401030 !!!
1609
      // 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
1610
      // then: parse resulting ELF file, take all program segments and concatenate them --> this is the blob (FIXME: wrong?)
1 pmbaty 1611
      // ######################################################################################################################################################################################################################################
11 pmbaty 1612
#if 0 // nonworking
16 pmbaty 1613
      // <deleted>
11 pmbaty 1614
#else // working
16 pmbaty 1615
      if (!Buffer_ReadFromFile (&file, startupfile_pathname))
1616
         DIE_WITH_EXITCODE (1, "failed to open \"%s\" for reading: %s", startupfile_pathname, strerror (errno));
1617
      ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&ifs.data, &file)); // write startup blob
1618
      Buffer_Forget (&file);
11 pmbaty 1619
#endif // working
16 pmbaty 1620
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1621
 
16 pmbaty 1622
      ifs.offsets.startuptrailer = ifs.data.size; // save startup trailer offset for future use
1623
      ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &startup_trailer, sizeof (startup_trailer))); // write startup trailer
1624
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1625
   }
1626
 
16 pmbaty 1627
   ifs.offsets.imageheader = ifs.data.size; // save image header offset for future use
1 pmbaty 1628
   memset (&image_header, 0, sizeof (image_header)); // prepare image header
1629
   memcpy (&image_header.signature, "imagefs", 7); // image filesystem signature, i.e. "imagefs"
1630
   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)
1631
   image_header.image_size    = WILL_BE_FILLED_LATER; // size from header to end of trailer (here 0xca6fe0 or 13 266 912)
7 pmbaty 1632
   image_header.hdr_dir_size  = WILL_BE_FILLED_LATER; // size from header to last dirent (here 0x12b8 or 4792)
1 pmbaty 1633
   image_header.dir_offset    = sizeof (image_header); // offset from header to first dirent (here 0x5c or 92)
1634
   image_header.boot_ino[0]   = image_kernel_ino; // inode of files for bootstrap p[ro?]g[ra?]ms (here 0xa0000002, 0, 0, 0)
1635
   image_header.script_ino    = image_bootscript_ino; // inode of file for script (here 3)
1636
   image_header.mountpoint[0] = '/'; // default mountpoint for image ("/" + "\0\0\0")
16 pmbaty 1637
   ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &image_header, sizeof (image_header))); // write image header
1638
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1639
 
6 pmbaty 1640
   // write image directory (with the wrong file offsets)
16 pmbaty 1641
   ifs.offsets.imagedir = ifs.data.size; // save image directory offset for future use
1642
   curr_offset = ifs.offsets.imagedir;
1 pmbaty 1643
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
16 pmbaty 1644
   {
1645
      Buffer_WriteIFSDirectoryEntryAt (&ifs.data, curr_offset, &fsentries[fsentry_index]); // write each dirent (the unknown fields will be fixed later)
1646
      curr_offset += fsentries[fsentry_index].header.size; // advance to the next one
1647
   }
1648
   ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&ifs.data, "\0\0\0\0")); // there seems to be 4 bytes of padding after the image directory
1649
   imgdir_size = ifs.data.size - ifs.offsets.imagedir; // measure image dir size and save it for future use
1 pmbaty 1650
 
1651
   // is it a bootable image with a kernel file ?
2 pmbaty 1652
   if ((startupfile_pathname != NULL) && (kernelfile_pathname != NULL))
1 pmbaty 1653
   {
7 pmbaty 1654
      // start by writing the startup script data blob, if we have one
1655
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1656
         if (fsentries[fsentry_index].header.ino == image_bootscript_ino)
8 pmbaty 1657
            break; // locate the startup script directory entry
7 pmbaty 1658
      if (fsentry_index < fsentry_count) // found it ?
1 pmbaty 1659
      {
16 pmbaty 1660
         if (ifs.data.size + fsentries[fsentry_index].u.file.size >= kernelfile_offset)
1661
            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 - ifs.data.size, ifs.data.size);
1662
         fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
1663
         Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
1 pmbaty 1664
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1665
      }
7 pmbaty 1666
 
1667
      // now write the filesystem entries that may fit before the kernel
1668
      for (;;)
1669
      {
16 pmbaty 1670
         available_space = kernelfile_offset - ifs.data.size; // measure the available space until the kernel
7 pmbaty 1671
 
1672
         // look for the biggest one that can fit
1673
         largest_index = 0;
8 pmbaty 1674
         largest_size = 0;
7 pmbaty 1675
         for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1676
         {
1677
            if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written || (fsentries[fsentry_index].u.file.size > available_space))
1678
               continue; // skip all entries that don't have a separate data block, those who were written already and those that wouldn't fit
1679
            if (fsentries[fsentry_index].u.file.size > largest_size)
1680
            {
1681
               largest_size = fsentries[fsentry_index].u.file.size;
1682
               largest_index = fsentry_index;
1683
            }
1684
         }
8 pmbaty 1685
         if (largest_size == 0)
7 pmbaty 1686
            break; // found none ? if so, stop searching
16 pmbaty 1687
         fsentry_index = largest_index;
7 pmbaty 1688
 
16 pmbaty 1689
         fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
1690
         Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
1691
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
7 pmbaty 1692
      }
16 pmbaty 1693
      LOG_INFO ("Last written offset: 0x%zx", ifs.data.size);
15 pmbaty 1694
      LOG_INFO ("Kernel file offset: 0x%zx", kernelfile_offset);
16 pmbaty 1695
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, kernelfile_offset)); // reach the kernel offset
1 pmbaty 1696
 
8 pmbaty 1697
      // now write the QNX kernel
1698
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1699
         if (fsentries[fsentry_index].header.ino == image_kernel_ino)
1700
            break; // locate the kernel directory entry (can't fail)
16 pmbaty 1701
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
15 pmbaty 1702
#ifdef PROCNTO_WIP
1703
      // is the kernel we're storing a preprocessed ELF kernel ?
1704
      if (fsentries[fsentry_index].header.ino & IFS_INO_PROCESSED_ELF)
1705
      {
1706
         elf = (elf_header_t *) fsentries[fsentry_index].u.file.UNSAVED_databuf; // quick access to ELF header
1707
         table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers
1708
         for (table_index = 0; table_index < table_count; table_index++)
1709
         {
1710
            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
1711
            corrective_offset = ELF_GET_NUMERIC (elf, phdr, virtual_addr) - ELF_GET_NUMERIC (elf, phdr, file_offset);
1712
            if (ELF_GET_NUMERIC (elf, phdr, size_in_memory) != 0) // only patch the physical address of segments that have an actual size in memory
16 pmbaty 1713
               ELF_SET_NUMERIC (elf, phdr, physical_addr, ELF_GET_NUMERIC (elf, phdr, physical_addr) + image_base + ifs.data.size - corrective_offset); // patch the physical address member of the program header table (NOTE: ifs.data.size is the location where the file data is about to be written)
15 pmbaty 1714
         }
1715
      }
1716
#endif // PROCNTO_WIP
16 pmbaty 1717
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write kernel file data
8 pmbaty 1718
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1 pmbaty 1719
   }
1720
 
8 pmbaty 1721
   // then write all the other files by increasing inode number: ELF files first
7 pmbaty 1722
   for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1 pmbaty 1723
   {
8 pmbaty 1724
      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
1725
          || (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 1726
         continue; // skip all entries that don't have a separate data block and those who were written already
16 pmbaty 1727
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
1728
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
1 pmbaty 1729
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1730
   }
8 pmbaty 1731
   for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) // other files (non-ELF, e.g. scripts and data files) last
1732
   {
1733
      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
1734
         continue; // skip all entries that don't have a separate data block and those who were written already
16 pmbaty 1735
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
1736
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
8 pmbaty 1737
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1738
   }
16 pmbaty 1739
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1740
 
1741
   // finally, write trailer (including empty checksum)
16 pmbaty 1742
   ifs.offsets.imagetrailer = ifs.data.size; // save image trailer offset for future use
1743
   ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &image_trailer, sizeof (image_trailer))); // write image trailer
1744
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 1745
 
1746
   // if we need to pad it to a specific length, do so
16 pmbaty 1747
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, image_totalsize));
1748
   ifs.final_size = ifs.data.size; // and this is the final size of the IFS
1 pmbaty 1749
 
1750
   // see if we are past the image max size, in which case it's an error
16 pmbaty 1751
   if (ifs.final_size > image_maxsize)
1752
      DIE_WITH_EXITCODE (1, "image file \"%s\" size %zd exceeds max size (%zd)", ifs_pathname, ifs.final_size, (size_t) image_maxsize);
1 pmbaty 1753
 
2 pmbaty 1754
   // do we have a startup file ? if so, this is a bootable image
1755
   if (startupfile_pathname != NULL)
1 pmbaty 1756
   {
16 pmbaty 1757
      // patch the startup header with its final values
1758
      startup_header.startup_size = (uint32_t) (ifs.offsets.imageheader - ifs.offsets.startupheader); // size of startup header up to image header
1759
      startup_header.imagefs_size = (uint32_t) (ifs.final_size - ifs.offsets.imageheader); // size of uncompressed imagefs
1760
      startup_header.ram_size     = (uint32_t) (ifs.final_size - ifs.offsets.startupheader);
1761
      startup_header.stored_size  = (uint32_t) (ifs.final_size - ifs.offsets.startupheader);
1762
      ASSERT_WITH_ERRNO (Buffer_WriteAt (&ifs.data, ifs.offsets.startupheader, &startup_header, sizeof (startup_header))); // write the final startup header at its right offset
8 pmbaty 1763
   }
1 pmbaty 1764
 
8 pmbaty 1765
   // rewrite image header with final values
16 pmbaty 1766
   image_header.image_size = (uint32_t) (ifs.final_size - ifs.offsets.imageheader); // size of uncompressed imagefs
8 pmbaty 1767
   image_header.hdr_dir_size = sizeof (image_header) + (uint32_t) imgdir_size; // size from start of image header to last dirent
16 pmbaty 1768
   ASSERT_WITH_ERRNO (Buffer_WriteAt (&ifs.data, ifs.offsets.imageheader, &image_header, sizeof (image_header))); // write image header
8 pmbaty 1769
 
1770
   // rewrite image directory with final offset values
1771
   if (image_header.flags & IMAGE_FLAGS_SORTED)
16 pmbaty 1772
      qsort (&fsentries[1], fsentry_count - 1, sizeof (fsentry_t), fsentry_compare_pathnames_cb); // sort the filesystem entries by pathname if necessary
1773
   curr_offset = ifs.offsets.imagedir; // position ourselves at the beginning of the image directory
8 pmbaty 1774
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
16 pmbaty 1775
   {
1776
      Buffer_WriteIFSDirectoryEntryAt (&ifs.data, curr_offset, &fsentries[fsentry_index]); // rewrite each dirent
1777
      curr_offset += fsentries[fsentry_index].header.size; // advance to the next one
1778
   }
8 pmbaty 1779
 
1780
   // ALL CHECKSUMS AT THE VERY END
1781
 
1782
   // do we have a startup file ? if so, this is a bootable image
1783
   if (startupfile_pathname != NULL)
1784
   {
1 pmbaty 1785
      // compute SHA-512 checksum and V1 checksum of startup block
6 pmbaty 1786
      if (   ( (startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
1787
          || (!(startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
1788
         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
1789
      else
1790
         is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 1791
 
16 pmbaty 1792
      if (startup_header.flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2) // is it a V2 trailer ?
1793
      {
1794
         SHA512 (&ifs.data.bytes[ifs.offsets.startupheader], ifs.offsets.startuptrailer - ifs.offsets.startupheader, &ifs.data.bytes[ifs.offsets.startuptrailer]); // compute SHA512 checksum and write it in place
1795
         checksum = update_checksum (&ifs.data.bytes[ifs.offsets.startupheader], ifs.offsets.startuptrailer + SHA512_DIGEST_LENGTH - ifs.offsets.startupheader, is_foreign_endianness); // compute old checksum
1796
         memcpy (&ifs.data.bytes[ifs.offsets.startuptrailer + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
1797
      }
1798
      else // old V1 trailer
1799
      {
1800
         checksum = update_checksum (&ifs.data.bytes[ifs.offsets.startupheader], ifs.offsets.startuptrailer - ifs.offsets.startupheader, is_foreign_endianness); // compute old checksum
1801
         memcpy (&ifs.data.bytes[ifs.offsets.startuptrailer], &checksum, 4); // and write it in place
1802
      }
1 pmbaty 1803
   }
1804
 
1805
   // compute SHA-512 checksum and V1 checksum of image block
5 pmbaty 1806
   if (   ( (image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
1807
       || (!(image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
1808
      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
1809
   else
1810
      is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 1811
 
16 pmbaty 1812
   if (image_header.flags & IMAGE_FLAGS_TRAILER_V2) // is it a V2 trailer ?
2 pmbaty 1813
   {
16 pmbaty 1814
      SHA512 (&ifs.data.bytes[ifs.offsets.imageheader], ifs.offsets.imagetrailer - ifs.offsets.imageheader, &ifs.data.bytes[ifs.offsets.imagetrailer]); // compute SHA512 checksum and write it in place
1815
      checksum = update_checksum (&ifs.data.bytes[ifs.offsets.imageheader], ifs.offsets.imagetrailer + SHA512_DIGEST_LENGTH - ifs.offsets.imageheader, is_foreign_endianness); // compute old checksum
1816
      memcpy (&ifs.data.bytes[ifs.offsets.imagetrailer + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
2 pmbaty 1817
   }
16 pmbaty 1818
   else // old V1 trailer
3 pmbaty 1819
   {
16 pmbaty 1820
      checksum = update_checksum (&ifs.data.bytes[ifs.offsets.imageheader], ifs.offsets.imagetrailer - ifs.offsets.imageheader, is_foreign_endianness); // compute old checksum
1821
      memcpy (&ifs.data.bytes[ifs.offsets.imagetrailer], &checksum, 4); // and write it in place
3 pmbaty 1822
   }
2 pmbaty 1823
 
16 pmbaty 1824
   // now rewrite IFS with the correct checksums
1825
   ASSERT_WITH_ERRNO (Buffer_WriteToFile (&ifs.data, ifs_pathname));
14 pmbaty 1826
 
16 pmbaty 1827
   // finished, cleanup
1828
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
14 pmbaty 1829
   {
1830
   }
1831
 
16 pmbaty 1832
   // and exit with a success code
1833
   LOG_INFO ("Success");
1834
   exit (0);
14 pmbaty 1835
}