Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 21 | Rev 24 | 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
 
16 pmbaty 6
// standard C includes
1 pmbaty 7
#include <stdint.h>
8
#include <stdbool.h>
9
#include <stdlib.h>
2 pmbaty 10
#include <stdarg.h>
1 pmbaty 11
#include <stdio.h>
12
#include <string.h>
16 pmbaty 13
#include <limits.h>
1 pmbaty 14
#include <errno.h>
15
#include <sys/stat.h>
16
#include <ctype.h>
17
#include <time.h>
18
 
16 pmbaty 19
// platform-specific includes
1 pmbaty 20
#ifdef _MSC_VER
14 pmbaty 21
#include <sys/utime.h>
20 pmbaty 22
#include <process.h>
1 pmbaty 23
#else // !_MSC_VER
24
#include <sys/param.h>
19 pmbaty 25
#include <sys/sysmacros.h>
20 pmbaty 26
#include <sys/wait.h>
1 pmbaty 27
#include <unistd.h>
19 pmbaty 28
#include <dirent.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
 
22 pmbaty 66
// macros for constructing and destructing string arrays
67
#define STRINGARRAY_INIT(string_array) do { (string_array)->args = NULL; (string_array)->count = 0; } while (0)
68
#define STRINGARRAY_PUSH(string_array,str) do { \
69
      reallocated_ptr = realloc ((string_array)->args, ((string_array)->count + 1) * sizeof (char *)); \
70
      ASSERT_WITH_ERRNO (reallocated_ptr); \
71
      (string_array)->args = reallocated_ptr; \
72
      (string_array)->args[(string_array)->count] = ((str) != NULL ? strdup ((str)) : NULL); \
73
      if ((str) != NULL) \
74
         ASSERT_WITH_ERRNO ((string_array)->args[(string_array)->count]); \
75
      (string_array)->count++; \
76
   } while (0)
77
#define STRINGARRAY_FREE(string_array) do { \
78
      if ((string_array)->args != NULL) { \
79
         for (array_index = 0; array_index < (string_array)->count; array_index++) \
80
            if ((string_array)->args[array_index] != NULL) \
81
               free ((string_array)->args[array_index]); \
82
         free ((string_array)->args); \
83
         (string_array)->args = NULL; \
84
      } \
85
      (string_array)->count = 0; \
86
   } while (0)
1 pmbaty 87
 
88
 
22 pmbaty 89
// string array structure type definition
90
typedef struct stringarray_s
91
{
92
   char **args;
93
   size_t count;
94
} stringarray_t;
95
 
96
 
11 pmbaty 97
// IFS directory entry insertion parameters structure type definition
1 pmbaty 98
typedef struct parms_s
99
{
100
   int dperms; // directory permissions (e.g. 0755)
101
   int perms; // file permissions (e.g. 0644)
102
   int uid; // owner user ID (e.g. 0 = root)
103
   int gid; // owner group ID (e.g. 0 = root)
104
   int st_mode; // entry type (e.g. S_IFREG for files) and permissions
7 pmbaty 105
   uint32_t mtime; // entry's modification time POSIX timestamp - set to UINT32_MAX to use the concerned files' mtime on the build host
106
   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)
17 pmbaty 107
   char *prefix; // [prefix=path] install path (e.g. "proc/boot")
7 pmbaty 108
   bool should_follow_symlinks; // follow symlinks
109
   bool should_autosymlink_dylib; // dynamic libraries should be written under their official SONAME and a named symlink be created pointing at them
15 pmbaty 110
   bool should_keep_ld_output; // whether to keep .sym files produced by ld calls, togglable by the [+keeplinked] attribute
18 pmbaty 111
   bool should_ignore_duplicates; // [+|-dupignore] whether to ignore duplicates
112
   bool should_allow_nonexistent_files; // [+|-optional] whether to continue processing on unexistent files
20 pmbaty 113
   bool is_bootstrap_file; // entry has the [virtual] attribute
2 pmbaty 114
   bool is_compiled_bootscript; // entry has [+script] attribute
10 pmbaty 115
   int extra_ino_flags; // bitmap of extra inode flags (IFS_INO_xxx)
17 pmbaty 116
   char *search; // [search=path[:path]] binary search path (the default one will be constructed at startup)
2 pmbaty 117
 
16 pmbaty 118
   buffer_t data; // the resolved file's own data bytes
1 pmbaty 119
} parms_t;
120
 
121
 
16 pmbaty 122
// exported globals
123
int verbose_level = 1; // verbosity level, can be increased with multiple -v[...] flags
124
 
125
 
126
// global variables used in this module only
1 pmbaty 127
static char line_buffer[4096]; // scrap buffer for the IFS build file parser
128
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
129
static uint32_t image_end = UINT32_MAX; // default image end (no limit)
130
static uint32_t image_maxsize = UINT32_MAX; // default image max size (no limit)
131
static uint32_t image_totalsize = 0; // image total size, measured once all the blocks have been written to the output IFS file
132
static uint32_t image_align = 4; // default image alignment, as per QNX docs
133
static uint32_t image_kernel_ino = 0;
134
static uint32_t image_bootscript_ino = 0;
7 pmbaty 135
#if defined(__x86_64__)
1 pmbaty 136
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)
17 pmbaty 137
static char image_processor_base[16] = "x86_64"; // default base 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 138
#elif defined(__aarch64__)
139
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)
17 pmbaty 140
static char image_processor_base[16] = "aarch64"; // default base 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 141
#else // unknown platform
142
#error Please port ifstool to this platform
143
#endif
1 pmbaty 144
static char *buildfile_pathname = NULL; // pathname of IFS build file
7 pmbaty 145
static char *current_line = NULL; // copy of current line in IFS build file
1 pmbaty 146
static int lineno = 0; // current line number in IFS build file
147
static char *QNX_TARGET = NULL; // value of the $QNX_TARGET environment variable
17 pmbaty 148
static char *SEARCH_PATH = NULL; // mallocated string of search paths, populated by the -r command-line argument
149
static char **saved_ELF_sections = NULL; // mallocated array of const strings, populated by the -s command-line argument
150
static size_t saved_ELF_section_count = 0; // number of elements in the saved_ELF_sections array
19 pmbaty 151
static char *sym_suffix = ""; // .sym files extra suffix, settable with the -a command-line argument
1 pmbaty 152
 
15 pmbaty 153
// bootable IFS support
22 pmbaty 154
static char *bootfile_pathname = NULL;           // FIXME: HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS
155
static size_t bootfile_size = 0;                 // FIXME: HACK: size of the bootcode binary blob file to put at the start of a bootable IFS
156
static char *startupfile_pathname = NULL;        // FIXME: HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS
157
static size_t startupfile_ep_from_imagebase = 0; // FIXME: HACK: startup code entrypoint offset from image base for a bootable IFS 
21 pmbaty 158
static size_t kernelfile_offset = 0x32000;       // kernel file offset in the IFS (is it ever supposed to change?)
1 pmbaty 159
 
15 pmbaty 160
 
16 pmbaty 161
// exported function prototypes
162
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
163
 
164
 
1 pmbaty 165
// prototypes of local functions
166
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)
17 pmbaty 167
static char *resolve_pathname (const char *pathname, const char *search_paths_or_NULL_for_MKIFS_PATH_envvar); // locates pathname among the known search paths and returns a pointer to the resolved pathname (static string)
10 pmbaty 168
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 169
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
20 pmbaty 170
static size_t Buffer_AppendIFSFileData (buffer_t *ifs_data, const fsentry_t *fsentry); // writes the given filesystem entry's file data (i.e. its contents) to the IFS buffer
171
static int Buffer_StripELFFile (buffer_t *file, const char **saved_sections, const size_t saved_section_count, const bool should_align_segsize_with_ramsize, const char *indicative_pathname); // strips an ELF file buffer the way mkifs does it and returns whether it succeeded
19 pmbaty 172
static void 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 in the the fsentries array
173
static void add_directory_contents_recursively (fsentry_t **fsentries, size_t *fsentry_count, const char *dir_pathname, const size_t start_pathname_len, parms_t *default_parms); // adds the contents of the directory pointed to by dir_pathname to the fsentries array, recursively
1 pmbaty 174
static int fsentry_compare_pathnames_cb (const void *a, const void *b); // qsort() comparison callback that sorts filesystem entries by pathnames
17 pmbaty 175
static void parse_line (FILE *buildfile_fp, char *line_buffer, fsentry_t **fsentries, size_t *fsentry_count, parms_t *default_parms); // parses a line in the build file and make the relevant changes to the fsentries array
1 pmbaty 176
 
177
 
16 pmbaty 178
// imported function prototypes
179
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
180
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
181
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 182
 
183
 
16 pmbaty 184
int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness)
1 pmbaty 185
{
6 pmbaty 186
   // 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 187
 
5 pmbaty 188
   uint8_t accumulator[4] = { 0, 0, 0, 0 };
7 pmbaty 189
   const char *current_char_ptr;
190
   int32_t image_cksum;
5 pmbaty 191
   size_t i;
192
 
7 pmbaty 193
   image_cksum = 0;
194
   current_char_ptr = data;
5 pmbaty 195
   for (i = 0; i < data_len; i++)
196
   {
197
      accumulator[i % 4] = *current_char_ptr;
198
      if (i % 4 == 3)
199
         if (is_foreign_endianness)
8 pmbaty 200
            image_cksum += (accumulator[3] << 0) + (accumulator[2] << 8) + (accumulator[1] << 16) + (accumulator[0] << 24);
5 pmbaty 201
         else
8 pmbaty 202
            image_cksum += (accumulator[0] << 0) + (accumulator[1] << 8) + (accumulator[2] << 16) + (accumulator[3] << 24);
5 pmbaty 203
      current_char_ptr++;
204
   }
205
 
206
   return (is_foreign_endianness ? __builtin_bswap32 (-image_cksum) : -image_cksum);
207
}
208
 
209
 
1 pmbaty 210
static long long read_integer (const char *str)
211
{
212
   // 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)
213
 
214
   char *endptr = NULL;
215
   long long ret = strtoll (str, &endptr, 0); // use strtoll() to handle hexadecimal (0x...), octal (0...) and decimal (...) bases
216
   if (endptr != NULL)
217
   {
15 pmbaty 218
      if      ((*endptr == 'k') || (*endptr == 'K')) ret *= (size_t) 1024;
1 pmbaty 219
      else if ((*endptr == 'm') || (*endptr == 'M')) ret *= (size_t) 1024 * 1024;
220
      else if ((*endptr == 'g') || (*endptr == 'G')) ret *= (size_t) 1024 * 1024 * 1024;
221
      else if ((*endptr == 't') || (*endptr == 'T')) ret *= (size_t) 1024 * 1024 * 1024 * 1024; // future-proof enough, I suppose?
222
   }
223
   return (ret);
224
}
225
 
226
 
17 pmbaty 227
static char *resolve_pathname (const char *pathname, const char *search_paths_or_NULL_for_MKIFS_PATH_envvar)
2 pmbaty 228
{
16 pmbaty 229
   // locates pathname among search path and returns resolved pathname (static buffer) or NULL.
2 pmbaty 230
 
17 pmbaty 231
   typedef struct default_path_s { bool uses_processor_base; char *subpath; } default_path_t;
232
 
233
   static const default_path_t default_paths[] =
234
   {
235
      { false, "/sbin"     }, // prefix with $PROCESSOR/
236
      { false, "/usr/sbin" }, // prefix with $PROCESSOR/
237
      { false, "/boot/sys" }, // prefix with $PROCESSOR/
238
      { true,  "/boot/sys" }, // prefix with $PROCESSOR_BASE/
239
      { false, "/bin"      }, // prefix with $PROCESSOR/
240
      { false, "/usr/bin"  }, // prefix with $PROCESSOR/
241
      { false, "/lib"      }, // prefix with $PROCESSOR/
242
      { false, "/lib/dll"  }, // prefix with $PROCESSOR/
243
      { false, "/usr/lib"  }  // prefix with $PROCESSOR/
244
   };
16 pmbaty 245
   static thread_local char *resolved_pathname = NULL;
2 pmbaty 246
 
18 pmbaty 247
   void *reallocated_ptr;
248
   char *resolved_search_path;
249
   char *replacement;
250
   size_t old_searchpath_len;
251
   size_t new_searchpath_len;
252
   size_t replacement_len;
253
   size_t middlebit_len;
254
   size_t endbit_len;
17 pmbaty 255
   size_t defaultpath_index;
16 pmbaty 256
   struct stat stat_buf;
18 pmbaty 257
   int erase_index;
258
   char erased_char;
259
   char *varname;
260
   char *nextsep;
261
   char *endbit;
262
   char *token;
2 pmbaty 263
 
16 pmbaty 264
   // initial allocation (per thread)
265
   if (resolved_pathname == NULL)
2 pmbaty 266
   {
16 pmbaty 267
      resolved_pathname = malloc (MAXPATHLEN);
268
      ASSERT_WITH_ERRNO (resolved_pathname);
2 pmbaty 269
   }
270
 
7 pmbaty 271
   // is it an absolute pathname (POSIX and Windows variants) ?
17 pmbaty 272
   if (IS_DIRSEP (pathname[0])
273
#ifdef _WIN32
274
       || (isalpha (pathname[0]) && (pathname[1] == ':') && IS_DIRSEP (pathname[2]))
275
#endif // _WIN32
276
       )
16 pmbaty 277
      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 278
   else // the path is relative, search it among the search paths we have
279
   {
17 pmbaty 280
      // QNX docs:
281
      // When searching for host files to be included in the image, search
282
      // the default paths used for storing binaries within the specified
283
      // directory before searching the default paths within $QNX_TARGET.
284
      // You can define multiple -r options; each adds a set of paths to
285
      // search for files. The -r options are evaluated from left to right
286
      // meaning the paths prefixed with the first (leftmost) rootdir are
287
      // searched first, then those prefixed with the second rootdir, and
288
      // so on.
289
      // Normally, mkifs searches any paths defined in $MKIFS_PATH when
290
      // it was called and then the default paths within $QNX_TARGET. The
291
      // default paths are based on the CPU architecture specified by
292
      // $PROCESSOR and $PROCESSOR_BASE. If you specify -r options, mkifs
293
      // searches the default paths prefixed with each dir variable before
294
      // searching those within $QNX_TARGET. These paths are:
295
      //   dir/${PROCESSOR}/sbin
296
      //   dir/${PROCESSOR}/usr/sbin
297
      //   dir/${PROCESSOR}/boot/sys
298
      //   dir/${PROCESSOR_BASE}/boot/sys
299
      //   dir/${PROCESSOR}/bin
300
      //   dir/${PROCESSOR}/usr/bin
301
      //   dir/${PROCESSOR}/lib
302
      //   dir/${PROCESSOR}/lib/dll
303
      //   dir/${PROCESSOR}/usr/lib
304
      // NOTE: The structure of the directory paths under dir must be
305
      // identical to that of the default paths under $QNX_TARGET, but the
306
      // root dir itself may be any path you choose. For example, if you
307
      // wanted to include /scratch/aarch64le/sbin/devb-sata, you would
308
      // specify a -r option like this:
309
      //   -r /scratch
310
      // Note that you don't include $PROCESSOR or $PROCESSOR_BASE in dir.
311
 
312
      //  - search all paths in explicit path/[default paths] (if explicit path supplied)
313
      //  - search all paths in (-r flags if have some|MKIFS_PATH)/[default paths] (if no explicit path supplied)
314
      //  - search all paths in $QNX_TARGET/[default paths]
315
 
316
      // if no file-specific explicit search path was supplied, use the path list supplied by the -r command-line arguments, else fallback to MKIFS_PATH if we don't have any
317
      if (search_paths_or_NULL_for_MKIFS_PATH_envvar == NULL)
318
         search_paths_or_NULL_for_MKIFS_PATH_envvar = (SEARCH_PATH != NULL ? SEARCH_PATH : getenv ("MKIFS_PATH"));
319
 
7 pmbaty 320
      // construct a potential final path using each element of the search path
17 pmbaty 321
      if (search_paths_or_NULL_for_MKIFS_PATH_envvar != NULL)
7 pmbaty 322
      {
18 pmbaty 323
         // the first step is to resolve all environment variables in the search path
324
         resolved_search_path = strdup (search_paths_or_NULL_for_MKIFS_PATH_envvar);
325
         ASSERT_WITH_ERRNO (resolved_search_path);
326
         while ((((token = strstr (resolved_search_path, "${")) != NULL) && ((endbit = strchr (token, '}')) != NULL)) // look for variables in the "${VARNAME}" format *AND* in "$VARNAME" format
327
                || (((token = strstr (resolved_search_path, "$")) != NULL) && ((middlebit_len = strspn (token, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_")) != strlen (token))))
328
         {
329
            if (token[1] == '{') // "${VARNAME}" format
330
            {
331
               endbit++; // locate where the end bit begins
332
               varname = token + 2; // skip the leading two characters: "${"
333
               erase_index = -1; // we shall split the string at the character that's *just before* where the end bit starts
334
            }
335
            else // "$VARNAME" format
336
            {
337
               endbit = &token[middlebit_len]; // locate where the end bit begins
338
               varname = token + 1; // skip the leading '$'
339
               erase_index = 0; // we shall split the string at the character that's *right where* the end bit starts
340
            }
341
            old_searchpath_len = strlen (resolved_search_path); // measure current string length
342
            endbit_len = strlen (endbit); // measure the length of the end bit (skip the closing curly brace)
343
            erased_char = endbit[erase_index]; // remember which is the character we're going to erase
344
            endbit[erase_index] = 0; // split the string at the end of the variable name
345
            replacement = getenv (varname); // peek at the environment for its value
346
            if (replacement == NULL)
347
               replacement = ""; // if this variable isn't defined, fallback to an empty string, just like what a UNIX shell does
348
            endbit[erase_index] = erased_char; // put the erased character back
349
            replacement_len = strlen (replacement); // measure replacement length
350
            new_searchpath_len = (size_t) token - (size_t) resolved_search_path + replacement_len + endbit_len; // measure updated search path len
351
            if (new_searchpath_len > old_searchpath_len)
352
            {
353
               reallocated_ptr = realloc (resolved_search_path, new_searchpath_len + 1); // grow it if necessary
354
               ASSERT_WITH_ERRNO (reallocated_ptr);
355
               token = &((char *) reallocated_ptr)[token - resolved_search_path]; // fix the pointers that may have moved
356
               endbit = &((char *) reallocated_ptr)[endbit - resolved_search_path]; // fix the pointers that may have moved
357
               resolved_search_path = reallocated_ptr;
358
            }
359
            memmove (token + replacement_len, endbit, endbit_len + 1); // move the end bit to its final location (including its nul terminator)
360
            memcpy (token, replacement, replacement_len); // and patch the replacement in between
361
         }
362
 
363
         // now split this search path string into multiple tokens and process them one after the other
364
         token = (*resolved_search_path != 0 ? resolved_search_path : NULL);
17 pmbaty 365
         nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP)] : NULL);
366
         while (token != NULL)
367
         {
18 pmbaty 368
            // look under this search path at each of the known subpaths
17 pmbaty 369
            for (defaultpath_index = 0; defaultpath_index < sizeof (default_paths) / sizeof (default_paths[0]); defaultpath_index++)
370
            {
371
               sprintf_s (resolved_pathname, MAXPATHLEN, "%.*s/%s/%s/%s", (int) (nextsep - token), token, (default_paths[defaultpath_index].uses_processor_base ? image_processor_base : image_processor), default_paths[defaultpath_index].subpath, pathname);
372
               if ((stat (resolved_pathname, &stat_buf) == 0) && S_ISREG (stat_buf.st_mode))
373
                  return (resolved_pathname); // if a file can indeed be found at this location, stop searching
374
            }
375
 
376
            token = (*nextsep != 0 ? nextsep + 1 : NULL);
377
            nextsep = (token != NULL ? &token[strcspn (token, PATH_SEP)] : NULL);
378
         }
379
      }
380
 
18 pmbaty 381
      // file not found in search paths: look under QNX_TARGET at each of the known subpaths
17 pmbaty 382
      for (defaultpath_index = 0; defaultpath_index < sizeof (default_paths) / sizeof (default_paths[0]); defaultpath_index++)
383
      {
384
         sprintf_s (resolved_pathname, MAXPATHLEN, "%s/%s/%s/%s", QNX_TARGET, (default_paths[defaultpath_index].uses_processor_base ? image_processor_base : image_processor), default_paths[defaultpath_index].subpath, pathname);
16 pmbaty 385
         if ((stat (resolved_pathname, &stat_buf) == 0) && S_ISREG (stat_buf.st_mode))
386
            return (resolved_pathname); // if a file can indeed be found at this location, stop searching
7 pmbaty 387
      }
388
   }
389
 
16 pmbaty 390
   errno = ENOENT; // we exhausted all possibilities
391
   return (NULL); // file not found, return with ENOENT
392
}
7 pmbaty 393
 
16 pmbaty 394
 
395
static size_t Buffer_WriteIFSDirectoryEntryAt (buffer_t *ifs, const size_t write_offset, const fsentry_t *fsentry)
396
{
397
   // writes a directory entry in the image filesystem buffer pointed to by ifs at write_offset (or fakes so if ifs is NULL)
398
   // and return the number of bytes written (or that would have been written)
399
 
400
   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";
401
 
402
   size_t datalen;
403
   size_t count;
404
 
405
   count = 0;
406
   if (ifs != NULL)
407
      ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->header, sizeof (fsentry->header))); // write the entry header (PACKED STRUCT)
408
   count += sizeof (fsentry->header);
409
   if (S_ISREG (fsentry->header.mode))
15 pmbaty 410
   {
16 pmbaty 411
      if (ifs != NULL)
412
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.file.offset, sizeof (uint32_t))); // write offset
413
      count += sizeof (uint32_t);
414
      if (ifs != NULL)
415
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.file.size,   sizeof (uint32_t))); // write size
416
      count += sizeof (uint32_t);
417
      datalen = strlen (fsentry->u.file.path) + 1;
418
      if (ifs != NULL)
419
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.file.path, datalen)); // write null-terminated path (no leading slash)
420
      count += datalen;
15 pmbaty 421
   }
16 pmbaty 422
   else if (S_ISDIR (fsentry->header.mode))
7 pmbaty 423
   {
16 pmbaty 424
      datalen = strlen (fsentry->u.dir.path) + 1;
425
      if (ifs != NULL)
426
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.dir.path, datalen)); // write null-terminated path (no leading slash)
427
      count += datalen;
7 pmbaty 428
   }
16 pmbaty 429
   else if (S_ISLNK (fsentry->header.mode))
7 pmbaty 430
   {
16 pmbaty 431
      if (ifs != NULL)
432
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.symlink.sym_offset, sizeof (uint16_t))); // write offset
433
      count += sizeof (uint16_t);
434
      if (ifs != NULL)
435
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.symlink.sym_size,   sizeof (uint16_t))); // write size
436
      count += sizeof (uint16_t);
437
      datalen = strlen (fsentry->u.symlink.path) + 1;
438
      if (ifs != NULL)
439
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.symlink.path, datalen)); // write null-terminated path (no leading slash)
440
      count += datalen;
441
      datalen = strlen (fsentry->u.symlink.contents) + 1;
442
      if (ifs != NULL)
443
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.symlink.contents, datalen)); // write null-terminated symlink contents
444
      count += datalen;
7 pmbaty 445
   }
16 pmbaty 446
   else
447
   {
448
      if (ifs != NULL)
449
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.device.dev,  sizeof (uint32_t))); // write dev number
450
      count += sizeof (uint32_t);
451
      if (ifs != NULL)
452
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, &fsentry->u.device.rdev, sizeof (uint32_t))); // write rdev number
453
      count += sizeof (uint32_t);
454
      datalen = strlen (fsentry->u.device.path) + 1;
455
      if (ifs != NULL)
456
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, fsentry->u.device.path, datalen)); // write null-terminated path (no leading slash)
457
      count += datalen;
458
   }
7 pmbaty 459
 
16 pmbaty 460
   ASSERT (count <= fsentry->header.size, "attempt to write invalid dirent (claimed size %zd, written size %zd). Aborting.", (size_t) fsentry->header.size, count);
461
   if (count < fsentry->header.size)
462
   {
463
      if (ifs != NULL)
464
         ASSERT_WITH_ERRNO (Buffer_WriteAt (ifs, write_offset + count, zeropad_buffer, fsentry->header.size - count)); // pad as necessary
465
      count += fsentry->header.size - count;
466
   }
467
 
468
   return (count);
7 pmbaty 469
}
470
 
471
 
20 pmbaty 472
static size_t Buffer_AppendIFSFileData (buffer_t *ifs_data, const fsentry_t *fsentry)
1 pmbaty 473
{
16 pmbaty 474
   // writes the given filesystem entry's file data (i.e. its contents) to the IFS buffer
1 pmbaty 475
 
16 pmbaty 476
   elf_program_header_t *phdr;
477
   elf_header_t *elf;
17 pmbaty 478
   size_t fixed_physical_addr;
16 pmbaty 479
   size_t corrective_offset;
480
   //size_t segment_type;
20 pmbaty 481
   size_t size_in_memory;
16 pmbaty 482
   size_t table_index;
483
   size_t table_count;
484
   size_t data_offset;
1 pmbaty 485
 
16 pmbaty 486
   ASSERT (S_ISREG (fsentry->header.mode), "function called for invalid dirent"); // consistency check
487
   data_offset = ifs_data->size; // see where we are
1 pmbaty 488
 
16 pmbaty 489
   // is the file we're storing a preprocessed ELF file ?
20 pmbaty 490
   if (fsentry->header.ino & IFS_INO_PROCESSED_ELF)
1 pmbaty 491
   {
16 pmbaty 492
 
493
      elf = (elf_header_t *) fsentry->u.file.UNSAVED_databuf; // quick access to ELF header
494
      table_count = ELF_GET_NUMERIC (elf, elf, program_header_table_len); // get the number of program headers
495
      for (table_index = 0; table_index < table_count; table_index++)
496
      {
497
         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
498
         //segment_type = ELF_GET_NUMERIC (elf, phdr, segment_type); // get segment type
499
         //if (!((segment_type >= 2) && (segment_type <= 7) || ((segment_type >= 0x6474e550) && (segment_type <= 0x6474e552)) || (segment_type == 0x70000001)))
500
         //   continue; // NOTE: only certain segments types must be corrected
501
 
502
 
503
         corrective_offset = ELF_GET_NUMERIC (elf, phdr, virtual_addr) - ELF_GET_NUMERIC (elf, phdr, file_offset);
20 pmbaty 504
         size_in_memory = ELF_GET_NUMERIC (elf, phdr, size_in_memory); // get this ELF segment's occupied size in memory
505
         if (size_in_memory != 0) // only patch the physical address of segments that have an actual size in memory
17 pmbaty 506
         {
507
            fixed_physical_addr = ELF_GET_NUMERIC (elf, phdr, physical_addr) + image_base + data_offset - corrective_offset;
508
            ELF_SET_NUMERIC (elf, phdr, physical_addr, fixed_physical_addr); // 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)
509
         }
16 pmbaty 510
      }
1 pmbaty 511
   }
512
 
16 pmbaty 513
   ASSERT_WITH_ERRNO (Buffer_Append (ifs_data, fsentry->u.file.UNSAVED_databuf, fsentry->u.file.size)); // write file data blob
514
   return (ifs_data->size - data_offset); // return the number of bytes written
1 pmbaty 515
}
516
 
517
 
16 pmbaty 518
static inline size_t Buffer_LocateOrAppendIfNecessaryAndReturnOffsetOf (buffer_t *buffer, const char *str)
10 pmbaty 519
{
16 pmbaty 520
   // helper function used in add_fsentry(): locates or appends str to buffer and returns its relative offset in the buffer
521
 
522
   size_t str_len_including_terminator = strlen (str) + 1;
523
   void *occurrence = Buffer_FindFirst (buffer, str, str_len_including_terminator);
524
   if (occurrence == NULL)
525
   {
526
      ASSERT_WITH_ERRNO (Buffer_Append (buffer, str, str_len_including_terminator));
527
      occurrence = Buffer_FindFirst (buffer, str, str_len_including_terminator);
528
      ASSERT_WITH_ERRNO (occurrence);
529
   }
530
   return (Buffer_OffsetOf (buffer, occurrence)); // can't fail
10 pmbaty 531
}
532
 
533
 
20 pmbaty 534
static int Buffer_StripELFFile (buffer_t *file, const char **saved_sections, const size_t saved_section_count, const bool should_align_segsize_with_ramsize, const char *indicative_pathname)
10 pmbaty 535
{
16 pmbaty 536
   // NOTE: for each ELF file, mkifs
537
   // -> 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)
538
   // -> 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
539
   // 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 540
 
16 pmbaty 541
   // reconstructed ELF:
542
   // ==== START OF FILE ====
543
   // ELF header
544
   // program header table
545
   //  (same sections, just p_addr offset changed)
546
   // section data 5 (named ".note.gnu.build-id")
547
   //  "............GNU....ZY.....c.o..l"
548
   // PROGRAM
549
   // sections table
550
   // + section 1: ALL ZEROES
551
   // + 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"
552
   // + 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"
553
   // + 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.."
554
   // + section 5: fileoffs 0x190 size 0x32 --> ".note.gnu.build-id" --> GNU build ID
555
   // + section 6: fileoffs 0x256e size 0x40 --> ".shstrtab" --> sections names strings table
556
   // section data 2 (named "QNX_info")
557
   //  (QNX binary description)
558
   // section data 3 (named ".gnu_debuglink")
559
   //  (debug file)
560
   // section data 4 (named "QNX_usage")
561
   //  (help text)
562
   // section data 6 (named ".shstrtab")
563
   //  "\0"
564
   //  ".shstrtab\0"
565
   //  "QNX_info\0"
566
   //  ".gnu_debuglink\0"
567
   //  "QNX_usage\0"
568
   //  ".note.gnu.build-id\0"
569
   // ==== END OF FILE ====
10 pmbaty 570
 
16 pmbaty 571
   #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
572
   #define ADD_SECTION(section_name,section_ptr) do { \
573
      void *reallocated_ptr = realloc (elf_sections, (elf_section_count + 1) * sizeof (elf_section_t)); \
574
      ASSERT_WITH_ERRNO (reallocated_ptr); \
575
      elf_sections = reallocated_ptr; \
576
      elf_sections[elf_section_count].name = (section_name); \
577
      Buffer_Initialize (&elf_sections[elf_section_count].data); \
578
      *(section_ptr) = &elf_sections[elf_section_count]; \
579
      elf_section_count++; \
580
   } while (0)
581
 
582
   typedef struct elf_section_s
10 pmbaty 583
   {
16 pmbaty 584
      const char *name;
585
      elf_section_header_t header;
586
      buffer_t data;
587
   } elf_section_t;
10 pmbaty 588
 
16 pmbaty 589
   const elf_section_header_t *shdr;
20 pmbaty 590
   elf_program_header_t *phdr;
591
   elf_program_header_t *other_phdr;
16 pmbaty 592
   elf_section_t *elf_sections = NULL; // mallocated
593
   elf_section_t *elf_section = NULL;
594
   size_t elf_section_count = 0;
595
   size_t new_shdrtable_offset;
17 pmbaty 596
   size_t new_shdrtable_len;
16 pmbaty 597
   size_t sectiondata_start;
598
   size_t sectiondata_size;
20 pmbaty 599
   size_t size_in_memory;
600
   size_t size_in_file;
601
   size_t file_offset;
16 pmbaty 602
   size_t array_index;
603
   size_t table_index;
604
   size_t table_count;
605
   size_t page_size;
10 pmbaty 606
 
16 pmbaty 607
   // find out the platform page size
608
   if (ELF_GET_NUMERIC (ELFHDR, ELFHDR, instruction_set) == ELF_MACHINE_X86_64)
609
      page_size = 4 * 1024; // 4 kb pages on Intel processors
610
   else if (ELF_GET_NUMERIC (ELFHDR, ELFHDR, instruction_set) == ELF_MACHINE_AARCH64)
611
      page_size = 16 * 1024; // 16 kb pages on ARM64
612
   else
613
   {
614
      errno = ENOTSUP; // unsupported architecture: set errno to something meaningful
615
      return (0); // and return an error value
616
   }
7 pmbaty 617
 
20 pmbaty 618
   // if we should align the segment sizes in the ELF file with their occupied memory size (such is the case for e.g. procnto), do that first
619
   table_count = ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_len); // get the number of program headers
620
   for (table_index = 0; table_index < table_count; table_index++)
621
   {
622
      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
623
      file_offset    = ELF_GET_NUMERIC (ELFHDR, phdr, file_offset); // get this ELF segment's start offset in the ELF file
624
      size_in_memory = ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory); // get this ELF segment's occupied size in memory
625
      size_in_file   = ELF_GET_NUMERIC (ELFHDR, phdr, size_in_file); // get this ELF segment's occupied size in the ELF file
626
      if (should_align_segsize_with_ramsize && (size_in_memory != size_in_file)) // should we align this segment's file size with its claimed RAM size ? (such is the case for e.g. procnto)
627
      {
628
         if (size_in_memory > size_in_file) // is it bigger ? if so, make sure we won't be overwriting other segments beyond this one
629
         {
630
            for (array_index = 0; array_index < table_count; array_index++)
631
            {
632
               other_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) * array_index]; // quick access to program header
633
               if (other_phdr == phdr)
634
                  continue; // skip self
635
               if (ELF_GET_NUMERIC (ELFHDR, other_phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, other_phdr, size_in_file) < file_offset)
636
                  continue; // skip segments that are located before this one
637
               if (ELF_GET_NUMERIC (ELFHDR, other_phdr, file_offset) > file_offset + size_in_memory)
638
                  continue; // skip segments that are located after this one, including its corrected size
639
               DIE_WITH_EXITCODE (1, "remapping ELF segment would overwrite segment #%zd in the same file", array_index);
640
            }
1 pmbaty 641
 
20 pmbaty 642
            // finally, memset() the extra area
643
            Buffer_WriteAt (file, file_offset + size_in_memory, NULL, 0); // reallocate the ELF file data buffer if necessary
22 pmbaty 644
            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]; // restore access to program header (which may have moved)
20 pmbaty 645
            memset (&file->bytes[file_offset + size_in_file], 0, size_in_memory - size_in_file); // and write zeroes over the extra space
646
         }
647
         ELF_SET_NUMERIC (ELFHDR, phdr, size_in_file, size_in_memory); // patch this segment's size in the ELF file so that it matches the RAM size
648
      }
649
   }
650
 
651
   // now parse the program header table, and measure the farthest offset known by this table where we'll write the reconstructed section headers table
16 pmbaty 652
   new_shdrtable_offset = 0;
653
   table_count = ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_len);
654
   for (table_index = 0; table_index < table_count; table_index++)
1 pmbaty 655
   {
16 pmbaty 656
      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
657
      if (ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_file) > new_shdrtable_offset)
658
         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 659
   }
16 pmbaty 660
   /*
661
   size_t new_shdrtable_offset_method2 = 0;
662
   for (table_index = 0; table_index < table_count; table_index++)
1 pmbaty 663
   {
16 pmbaty 664
      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
665
      size_t segment_type = ELF_GET_NUMERIC (ELFHDR, phdr, segment_type); // get segment type
666
      if (!((segment_type >= 2) && (segment_type <= 7)))
667
         continue; // NOTE: only certain segments types must be corrected
668
      if (ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory) > new_shdrtable_offset_method2)
669
         new_shdrtable_offset_method2 = ELF_GET_NUMERIC (ELFHDR, phdr, file_offset) + ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory);
1 pmbaty 670
   }
16 pmbaty 671
   if (new_shdrtable_offset_method2 > new_shdrtable_offset)
672
      LOG_DEBUG ("METHOD2: %llx > %llx", new_shdrtable_offset_method2, new_shdrtable_offset);*/
673
   //new_shdrtable_offset = ROUND_TO_UPPER_MULTIPLE (new_shdrtable_offset, page_size); // round to page size
674
 
675
   // re-create the section header table
676
   ADD_SECTION (".shstrtab", &elf_section); // the first section will be the section names strings table
677
   ASSERT_WITH_ERRNO (Buffer_InitWithByteArray (&elf_section->data, "\0")); // initialize an empty section headers strings table
678
   ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&elf_section->data, ".shstrtab\0")); // append ".shstrtab" *INCLUDING* its null terminator
679
 
680
   // go through the saved sections array and see if such an ELF section is present in the ELF file
17 pmbaty 681
   for (array_index = 0; array_index < saved_section_count; array_index++)
16 pmbaty 682
      if ((shdr = elf_get_section_header_by_name (ELFHDR, saved_sections[array_index])) != NULL) // does this ELF have such a section ?
1 pmbaty 683
      {
16 pmbaty 684
         ADD_SECTION (saved_sections[array_index], &elf_section); // yes, so save it
685
         sectiondata_start = ELF_GET_NUMERIC (ELFHDR, shdr, file_offset); // identify section data start offset
686
         sectiondata_size = ELF_GET_NUMERIC (ELFHDR, shdr, size); // identify section data length
687
         if (sectiondata_start + sectiondata_size >= new_shdrtable_offset) // should this section be moved ?
688
            ASSERT_WITH_ERRNO (Buffer_InitWithData (&elf_section->data, &file->bytes[sectiondata_start], sectiondata_size)); // have a copy of this section's data
689
         else
690
            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
17 pmbaty 691
         //LOG_DEBUG ("%s: section '%s' start 0x%llx len 0x%llx", indicative_pathname, saved_ELF_sections[array_index], (unsigned long long) sectiondata_start, (unsigned long long) sectiondata_size);
16 pmbaty 692
 
693
         // prepare this section's "fixed" header
694
         memcpy (&elf_section->header, shdr, ELF_STRUCT_SIZE (ELFHDR, shdr)); // have a copy of the old section header first
695
         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 696
      }
16 pmbaty 697
 
698
   // jump over the new section headers table and write the saved sections data after the section headers table
699
   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
700
   for (table_index = 1; table_index < elf_section_count; table_index++)
1 pmbaty 701
   {
16 pmbaty 702
      elf_section = &elf_sections[table_index]; // quick access to ELF section about to be written
703
      if (elf_section->data.bytes != NULL) // was this section data backed up waiting to be relocated ?
1 pmbaty 704
      {
16 pmbaty 705
         ELF_SET_NUMERIC (ELFHDR, &elf_section->header, file_offset, file->size); // fix section offset
706
         Buffer_AppendBuffer (file, &elf_section->data); // append this section's data to the ELF file
1 pmbaty 707
      }
708
   }
16 pmbaty 709
   // write the section header strings table as the last section
710
   elf_section = &elf_sections[0]; // quick access to ELF section about to be written
711
   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
712
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, type, ELF_SECTIONTYPE_STRINGTABLE); // section type (SHT_STRTAB)
713
   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)
714
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, virtual_addr, 0); // this section does not need to be mapped
715
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, file_offset, file->size); // fix section offset
716
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, size, elf_sections[0].data.size); // section size
717
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, linked_index, 0); // this section is not linked to any other
718
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, info, 0); // this section has no additional info
719
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, alignment, 1); // this section is byte-aligned
720
   ELF_SET_NUMERIC (ELFHDR, &elf_section->header, entry_size, 0); // this section is not a table, so entry_size is zero
721
   Buffer_AppendBuffer (file, &elf_section->data); // append section headers strings table section data to ELF file
1 pmbaty 722
 
16 pmbaty 723
   // now write the section headers table
724
   memset (&file->bytes[new_shdrtable_offset], 0, ELF_STRUCT_SIZE (ELFHDR, &elf_sections[0].header)); // the first section header is always zerofilled
725
   for (table_index = 1; table_index < elf_section_count; table_index++)
726
      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
727
   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 728
 
16 pmbaty 729
   // and finally fix the ELF master header
17 pmbaty 730
   new_shdrtable_len = 1 + elf_section_count; // take in account that the first entry in the section headers table is empty
16 pmbaty 731
   ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_table_offset, new_shdrtable_offset);
17 pmbaty 732
   ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_table_len, new_shdrtable_len);
16 pmbaty 733
   ELF_SET_NUMERIC (ELFHDR, ELFHDR, section_header_names_idx, elf_section_count); // the section headers strings table is the last section
734
 
735
   // align size with page size (4096 on x86, 16k on ARM), zerofilling the extra space
736
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (file, ROUND_TO_UPPER_MULTIPLE (file->size, page_size)));
737
 
738
   // cleanup
739
   for (table_index = 0; table_index < elf_section_count; table_index++)
740
      Buffer_Forget (&elf_sections[table_index].data); // free all sections' backing buffers
741
 
742
   #undef ELFHDR // undefine the macro that used to always point to the ELF header at the beginning of the file
743
   return (1); // success
1 pmbaty 744
}
745
 
746
 
19 pmbaty 747
static void add_fsentry (fsentry_t **fsentries, size_t *fsentry_count, parms_t *entry_parms, const char *stored_pathname, const char *buildhost_pathname)
1 pmbaty 748
{
16 pmbaty 749
   static thread_local char *candidate_pathname = NULL;
21 pmbaty 750
   static thread_local parms_t default_parms = { 0 };
22 pmbaty 751
   static thread_local stringarray_t global_envp = { NULL, 0 };
752
   static thread_local stringarray_t aps_partnames = { NULL, 0 };
1 pmbaty 753
   static int inode_count = 0; // will be preincremented each time this function is called
754
 
22 pmbaty 755
   typedef struct scriptcmd_s
756
   {
757
      const char *argv0;
758
      int cpu_number;
759
      bool is_external;
760
      int priority;
761
      int sched_policy;
762
      int aps_partindex;
763
      bool is_session_leader;
764
      bool is_background_task;
765
      bool has_debug_flag;
766
   } scriptcmd_t;
767
 
768
   scriptcmd_t default_scriptcmd_params = { NULL, -1, false, -1, -1, -1, false, false, false };
769
   scriptcmd_t current_scriptcmd_params = { 0 };
20 pmbaty 770
   stringarray_t global_argv = { NULL, 0 };
771
   stringarray_t line_argv = { NULL, 0 };
772
   stringarray_t line_envp = { NULL, 0 };
773
   stringarray_t startup_argv = { NULL, 0 };
774
   stringarray_t startup_envp = { NULL, 0 };
775
   stringarray_t procnto_argv = { NULL, 0 };
776
   stringarray_t procnto_envp = { NULL, 0 };
777
   stringarray_t linker_argv = { NULL, 0 };
18 pmbaty 778
   const char *stored_pathname_without_leading_slash;
7 pmbaty 779
   const char *original_stored_pathname = NULL;
20 pmbaty 780
   buffer_t current_line;
22 pmbaty 781
   buffer_t compiled_script;
782
   buffer_t compiled_scriptline;
16 pmbaty 783
   buffer_t *shstrtab = NULL;
7 pmbaty 784
   const char *canonical_dylib_name;
785
   const char *dynamic_strings; // strings table of the ".dynamic" section
786
   const char *last_dirsep;
20 pmbaty 787
   size_t array_index;
788
   size_t line_index;
18 pmbaty 789
   size_t fsentry_index;
22 pmbaty 790
   size_t wait_time;
7 pmbaty 791
   char *resolved_pathname;
20 pmbaty 792
   char *linebit_start;
793
   char *write_ptr;
794
   char *read_ptr;
795
   char *token;
796
   char *value;
797
   char *ctx;
1 pmbaty 798
   void *reallocated_ptr;
7 pmbaty 799
   void *old_data;
20 pmbaty 800
   bool is_quoted_context;
801
   bool is_end_of_line;
1 pmbaty 802
   struct stat stat_buf;
803
   fsentry_t *fsentry;
19 pmbaty 804
   int retval;
1 pmbaty 805
 
16 pmbaty 806
   // initial allocation (per thread)
807
   if (candidate_pathname == NULL)
808
   {
809
      candidate_pathname = malloc (MAXPATHLEN);
810
      ASSERT_WITH_ERRNO (candidate_pathname);
811
   }
812
 
1 pmbaty 813
   if (S_ISDIR (entry_parms->st_mode)) // are we storing a directory ?
814
   {
21 pmbaty 815
      if (buildhost_pathname != NULL) // was a source file pathname supplied ?
816
      {
817
         memcpy (&default_parms, entry_parms, sizeof (parms_t)); // apply current entry parameters when including a directory recursively
818
         add_directory_contents_recursively (fsentries, fsentry_count, buildhost_pathname, strlen (buildhost_pathname), &default_parms); // if so, add this diretory contents recursively
819
      }
15 pmbaty 820
      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 821
   }
21 pmbaty 822
   else if (S_ISLNK (entry_parms->st_mode)) // else are we storing a symbolic link ?
1 pmbaty 823
   {
21 pmbaty 824
      // do we already know the data for this data blob ?
825
      if (entry_parms->data.bytes != NULL)
826
      {
827
         entry_parms->mtime = entry_parms->mtime_for_inline_files; // if so, set it a mtime equal to the mtime to use for inline files
828
         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);
829
      }
830
      else if (buildhost_pathname != NULL) // else was a source file pathname supplied ?
831
      {
832
         entry_parms->data.bytes = malloc (MAXPATHLEN); // allocate enough space for symlink data
833
         ASSERT_WITH_ERRNO (entry_parms->data.bytes);
834
         retval = readlink (buildhost_pathname, entry_parms->data.bytes, MAXPATHLEN); // read symlink contents
835
         ASSERT_WITH_ERRNO (retval > 0);
836
         entry_parms->data.size = retval; // save symlink target length
837
      }
838
      else
839
         DIE_WITH_EXITCODE (1, "unexpected code path: can't store a symlink without neither explicit contents nor a host pathname. This is a bug in the program. Please contact the author.");
840
   }
841
   else if (S_ISFIFO (entry_parms->st_mode)) // else are we storing a FIFO ?
842
   {
843
      if ((entry_parms->data.bytes == NULL) || (strchr (entry_parms->data.bytes, ':') == NULL))
844
         DIE_WITH_EXITCODE (1, "device entry \"%s\" malformed (no 'dev:rdev' pair)", stored_pathname);
845
      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);
846
   }
847
   else // necessarily a regular file (either S_IFREG is specified, or st_mode is zero)
848
   {
849
      entry_parms->st_mode |= S_IFREG; // make this explicit
850
 
20 pmbaty 851
      if (entry_parms->is_bootstrap_file) // is it the bootstrap file ?
1 pmbaty 852
      {
20 pmbaty 853
         // parse each line of contents
854
         ASSERT (entry_parms->data.size > 0, "kernel specification without inline contents");
15 pmbaty 855
 
20 pmbaty 856
         // parse buffer (non-destructively) line after line
857
         Buffer_Initialize (&current_line);
858
         for (line_index = 0; Buffer_GetNthLine (&entry_parms->data, line_index, &current_line); line_index++)
15 pmbaty 859
         {
20 pmbaty 860
            read_ptr = current_line.bytes;
861
            while (isspace (*read_ptr))
862
               read_ptr++; // skip leading spaces
863
            if ((*read_ptr == '#') || (*read_ptr == 0))
15 pmbaty 864
               continue; // skip comments and empty lines
865
 
866
            // format of a line: [attributes] [env assignation] [...] [executable] [arg] [...] [comment]
867
            // example: "[uid=0 gid=0 perms=0700] CONFIG_PATH=/proc/boot:/etc procnto-smp-instr -v -mr -d 0777 -u 0777"
868
 
20 pmbaty 869
            LOG_DEBUG ("parsing line: %s", read_ptr);
870
 
15 pmbaty 871
            // does this line start with an attribute block ?
20 pmbaty 872
            if (*read_ptr == '[')
15 pmbaty 873
            {
20 pmbaty 874
               read_ptr++; // skip the leading square bracket
875
               linebit_start = read_ptr; // remember where it starts
15 pmbaty 876
               is_quoted_context = false; // reach the next unescaped closing square bracket that is not between quotes
20 pmbaty 877
               while ((*read_ptr != 0) && !((*read_ptr == ']') && (read_ptr[-1] != '\\') && !is_quoted_context))
15 pmbaty 878
               {
20 pmbaty 879
                  if (*read_ptr == '"')
15 pmbaty 880
                     is_quoted_context ^= true; // remember when we're between quotes
20 pmbaty 881
                  else if (!is_quoted_context && (*read_ptr == ' '))
882
                     *read_ptr = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
883
                  read_ptr++; // reach the next unescaped closing square bracket
15 pmbaty 884
               }
20 pmbaty 885
               if (*read_ptr != ']')
15 pmbaty 886
               {
22 pmbaty 887
                  LOG ("warning", 0, "syntax error in \"%s\" line %zd of inline document '%s': unterminated attributes block (skipping)", buildfile_pathname, 1 + line_index, stored_pathname);
15 pmbaty 888
                  continue; // invalid attribute block, skip line
889
               }
20 pmbaty 890
               is_end_of_line = (*read_ptr == 0); // see if we're at the end of line already
891
               *read_ptr = 0; // end the attribute block in all cases so that it is a parsable C string
15 pmbaty 892
 
893
               // now parse the attribute tokens (NOTE: THE LIST OF ALLOWED ATTRIBUTES HERE IS NOT DOCUMENTED)
16 pmbaty 894
               token = strtok_r (linebit_start, RECORD_SEP, &ctx);
15 pmbaty 895
               while (token != NULL)
896
               {
897
                  #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
20 pmbaty 898
                  if (false) {}
17 pmbaty 899
                  else if (strncmp (token, "prefix=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms->prefix  = (*value == '/' ? value + 1 : value); } // skip possible leading slash in prefix (NOTE: stolen pointer. Do not free.)
900
                  else if (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms->uid     = (int) read_integer (value); }
15 pmbaty 901
                  else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms->gid     = (int) read_integer (value); }
902
                  else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms->perms   = (int) read_integer (value); }
903
                  else if (strcmp (token, "+followlink") == 0) entry_parms->should_follow_symlinks = true;
904
                  else if (strcmp (token, "-followlink") == 0) entry_parms->should_follow_symlinks = false;
905
                  else if (strcmp (token, "+keeplinked") == 0) entry_parms->should_keep_ld_output = true;
906
                  else if (strcmp (token, "-keeplinked") == 0) entry_parms->should_keep_ld_output = false;
22 pmbaty 907
                  else LOG_WARNING ("unimplemented bootstrap executable attribute in \"%s\" line %zd of inline document '%s': '%s'", buildfile_pathname, 1 + line_index, stored_pathname, token);
15 pmbaty 908
                  #undef REACH_TOKEN_VALUE
16 pmbaty 909
                  token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
15 pmbaty 910
               }
911
 
20 pmbaty 912
               if (is_end_of_line)
913
                  continue; // if end of line was reached, proceed to the next line
914
               else
915
                  read_ptr++; // else reach the next character (after the NUL split) and continue processing the same line
15 pmbaty 916
            } // end of "this line starts with an attributes block"
917
 
20 pmbaty 918
            // at this point we are past the attributes block
919
 
920
            // reset contextual argv/envp arrays
921
            line_argv.args = NULL;
922
            line_argv.count = 0;
923
            line_envp.args = NULL;
924
            line_envp.count = 0;
925
 
926
            // now read each word (or quoted group of words), unescaping escaped characters
927
            while (*read_ptr != 0)
15 pmbaty 928
            {
20 pmbaty 929
               while ((*read_ptr != 0) && isspace (*read_ptr))
930
                  read_ptr++; // skip intermediate spaces and reach the next word
931
 
932
               if (*read_ptr == '#')
933
                  break; // if the rest of the line is commented out, stop parsing it and proceed to the next line
934
 
935
               linebit_start = read_ptr; // remember the word (or quoted group of words) starts here
936
               write_ptr = read_ptr;
937
               is_quoted_context = (*read_ptr == '"'); // see if we're entering a quoted context or not
15 pmbaty 938
               if (is_quoted_context)
20 pmbaty 939
                  read_ptr++; // skip a possible initial quote in the word
22 pmbaty 940
               while ((*read_ptr != 0) && ((!is_quoted_context && !isspace (*read_ptr)) || (is_quoted_context && (*read_ptr != '"'))))
15 pmbaty 941
               {
20 pmbaty 942
                  if (*read_ptr == '\\')
943
                     read_ptr++; // unescape characters that are escaped with '\' by advancing the read pointer
944
                  *write_ptr++ = *read_ptr++; // recopy characters as we read them
15 pmbaty 945
               }
20 pmbaty 946
               is_end_of_line = (*read_ptr == 0); // see if we're at the end of line already
947
               *write_ptr = 0; // stop the rewritten string here
15 pmbaty 948
 
20 pmbaty 949
               // end of word, i.e. we reached either a closing quote or a space. The string bit has been rewritted at linebit_start without quotes and with characters unescaped.
950
 
951
               if ((strchr (linebit_start, '=') != NULL) && (line_argv.count == 0)) // is it an assignation AND have we not started constructing argv yet?
15 pmbaty 952
               {
20 pmbaty 953
                  STRINGARRAY_PUSH (&line_envp, linebit_start); // linebit_start is of the form "NAME=VALUE": it's an environment variable assignation
954
                  LOG_DEBUG ("collected envp: [%s]", linebit_start);
955
               }
956
               else // it's an executable argument (argv)
957
               {
958
                  STRINGARRAY_PUSH (&line_argv, linebit_start); // linebit_start is either NOT of the form "NAME=VALUE" OR we started constructing argv: it's a command-line argument
959
                  LOG_DEBUG ("collected argv: [%s]", linebit_start);
960
               }
15 pmbaty 961
 
20 pmbaty 962
               if (!is_end_of_line)
963
                  read_ptr++; // if we haven't reach the end of the line yet, advance to the next character (after the NUL split)
964
            } // end while (*read_ptr != 0)
15 pmbaty 965
 
20 pmbaty 966
            // we finished parsing the line
15 pmbaty 967
 
20 pmbaty 968
            // did we fill an executable argv? As per QNX docs, the first executable must be startup-*, the last executable must be procnto.
969
            if (line_argv.count > 0)
970
            {
971
               if (startup_argv.args == NULL)
972
               {
973
                  startup_argv.args = line_argv.args; // relocate these pointers to the right place
974
                  startup_argv.count = line_argv.count;
975
                  startup_envp.args = line_envp.args; // relocate these pointers to the right place
976
                  startup_envp.count = line_envp.count;
15 pmbaty 977
               }
20 pmbaty 978
               else
15 pmbaty 979
               {
20 pmbaty 980
                  STRINGARRAY_FREE (&procnto_argv); // if procnto's argv was already assigned, free the previous array as we'll be replacing it with a new one
981
                  procnto_argv.args = line_argv.args; // relocate these pointers to the right place
982
                  procnto_argv.count = line_argv.count;
983
                  STRINGARRAY_FREE (&procnto_envp); // if procnto's envp was already assigned, free the previous array as we'll be replacing it with a new one
984
                  procnto_envp.args = line_envp.args; // relocate these pointers to the right place
985
                  procnto_envp.count = line_envp.count;
986
               }
987
               line_argv.args = NULL; // void the line_argv array so as to not free it as we stole its args pointers
988
               line_argv.count = 0;
989
               line_envp.args = NULL; // void the line_envp array so as to not free it as we stole its args pointers
990
               line_envp.count = 0;
991
            }
992
            else // this line contained no executable invokation, so stack up its envp assignations into the global envp array
993
               for (array_index = 0; array_index < line_envp.count; array_index++)
994
                  STRINGARRAY_PUSH (&global_envp, line_envp.args[array_index]);
15 pmbaty 995
 
20 pmbaty 996
            // release the contextual argv/envp arrays
997
            STRINGARRAY_FREE (&line_argv);
998
            STRINGARRAY_FREE (&line_envp);
15 pmbaty 999
 
20 pmbaty 1000
         } // end for (line_index = 0; Buffer_GetNthLine (&entry_parms->data, line_index, &current_line); line_index++)
22 pmbaty 1001
         Buffer_Forget (&entry_parms->data); // free the inline specification once it's parsed
15 pmbaty 1002
 
20 pmbaty 1003
         ASSERT (startup_argv.args && startup_argv.args[0] && *startup_argv.args[0], "the QNX startup executable (startup-*) is missing in this bootstrap inline specification");
1004
         ASSERT (procnto_argv.args && procnto_argv.args[0] && *procnto_argv.args[0], "the QNX kernel (procnto-*) is missing in this bootstrap inline specification");
15 pmbaty 1005
 
1006
         // now we know which startup and procnto executables to use
20 pmbaty 1007
         LOG_DEBUG ("Startup: %s", startup_argv.args[0]);
1008
         LOG_DEBUG ("Kernel: %s",  procnto_argv.args[0]);
15 pmbaty 1009
 
1010
         static thread_local char linker_pathname[MAXPATHLEN] = "";
1011
         static thread_local char linker_sysroot_arg[MAXPATHLEN] = "";
1012
         static thread_local char linker_script_pathname_arg[MAXPATHLEN] = "";
1013
         static thread_local char procnto_buildhost_pathname[MAXPATHLEN] = "";
1014
         static thread_local char procnto_sym_filename[MAXPATHLEN] = "";
20 pmbaty 1015
         buffer_t bootargs_buffer = { 0 };
1016
         char *bootargs_location;
15 pmbaty 1017
 
1018
         // construct the arguments that are based on environment variables (infer QNX_HOST from QNX_TARGET)
1019
#if defined(_WIN32)
22 pmbaty 1020
         sprintf_s (linker_pathname, sizeof (linker_pathname), "%s/../../host/win64/x86_64/usr/bin/%s-ld" /*"-2.41.0"*/ ".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
15 pmbaty 1021
#elif defined(__linux__)
22 pmbaty 1022
         sprintf_s (linker_pathname, sizeof (linker_pathname), "%s/../../host/linux/x86_64/usr/bin/%s-ld" /*"-2.41.0"*/, QNX_TARGET, (strcmp (image_processor, "x86_64") == 0 ? "x86_64-pc-nto-qnx8.0.0" : "aarch64-unknown-nto-qnx8.0.0"));
15 pmbaty 1023
#elif defined(__QNXNTO__)
22 pmbaty 1024
         sprintf_s (linker_pathname, sizeof (linker_pathname), "%s/../../host/qnx8/x86_64/usr/bin/%s-ld" /*"-2.41.0"*/, QNX_TARGET, (strcmp (image_processor, "x86_64") == 0 ? "x86_64-pc-nto-qnx8.0.0" : "aarch64-unknown-nto-qnx8.0.0"));
15 pmbaty 1025
#else // wtf are you building this on?
1026
#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.
1027
#endif
16 pmbaty 1028
         ASSERT (access (linker_pathname, 0) == 0, "host cross-linker for QNX8 \"%s\" not found", linker_pathname);
20 pmbaty 1029
         sprintf_s (linker_sysroot_arg, sizeof (linker_sysroot_arg), "--sysroot=%s/%s/", QNX_TARGET, image_processor);
1030
         sprintf_s (linker_script_pathname_arg, sizeof (linker_script_pathname_arg), "-T%s/%s/lib/nto.link", QNX_TARGET, image_processor);
15 pmbaty 1031
 
20 pmbaty 1032
         resolved_pathname = resolve_pathname (procnto_argv.args[0], entry_parms->search); // locate the procnto kernel location
1033
         ASSERT (resolved_pathname, "QNX kernel \"%s\" not found in search path", procnto_argv.args[0]);
1034
         strcpy_s (procnto_buildhost_pathname, sizeof (procnto_buildhost_pathname), resolved_pathname);
15 pmbaty 1035
 
20 pmbaty 1036
         sprintf_s (procnto_sym_filename, sizeof (procnto_sym_filename), "%s.sym%s", procnto_argv.args[0], sym_suffix);
15 pmbaty 1037
 
20 pmbaty 1038
         // construct the linker invokation command-line arguments array (argv)
1039
         STRINGARRAY_INIT (&linker_argv);
1040
         STRINGARRAY_PUSH (&linker_argv, strrchr (linker_pathname, '/') + 1); // "${TARGET_TRIPLE}-ld"
1041
         STRINGARRAY_PUSH (&linker_argv, linker_sysroot_arg); // "--sysroot=${QNX_TARGET}/${TARGET_CPU}/"
1042
         STRINGARRAY_PUSH (&linker_argv, linker_script_pathname_arg); // "-T${QNX_TARGET}/${TARGET_CPU}/lib/nto.link"
1043
         STRINGARRAY_PUSH (&linker_argv, "--section-start");
1044
         STRINGARRAY_PUSH (&linker_argv, ".text=0xffff800000001000");
1045
         STRINGARRAY_PUSH (&linker_argv, "--no-relax");
1046
         STRINGARRAY_PUSH (&linker_argv, procnto_buildhost_pathname); // "${QNX_TARGET}/${TARGET_CPU}/boot/sys/procnto-smp-instr"
1047
         STRINGARRAY_PUSH (&linker_argv, "-o");
1048
         STRINGARRAY_PUSH (&linker_argv, procnto_sym_filename); // "procnto-smp-instr.sym"
22 pmbaty 1049
#ifdef __GNUC__
1050
#pragma GCC diagnostic push
1051
#pragma GCC diagnostic ignored "-Wnonnull" // the GCC linter is wrong here: I *do* check for NULL before calling strdup()
1052
#endif // __GNUC__
20 pmbaty 1053
         STRINGARRAY_PUSH (&linker_argv, NULL); // don't forget to terminate the argv array with a NULL pointer
22 pmbaty 1054
#ifdef __GNUC__
1055
#pragma GCC diagnostic pop
1056
#endif // __GNUC__
15 pmbaty 1057
         if (verbose_level > 2)
1058
         {
1059
            fprintf (stderr, "ifstool: calling:");
20 pmbaty 1060
            for (array_index = 0; array_index < linker_argv.count - 1; array_index++)
1061
               fprintf (stderr, " '%s'", linker_argv.args[array_index]);
15 pmbaty 1062
            fputc ('\n', stderr);
1063
         }
20 pmbaty 1064
#ifdef _WIN32
1065
         _spawnv (_P_WAIT, linker_pathname, linker_argv.args); // spawn the linker and produce a stripped procnto (wait for completion)
1066
#else // !_WIN32, thus POSIX
1067
         do {
1068
            int status;
1069
            pid_t pid = fork (); // duplicate ourselves so as to create a new process
1070
            ASSERT_WITH_ERRNO (pid != -1);
22 pmbaty 1071
            if (pid == 0) // we are the child
20 pmbaty 1072
            {
22 pmbaty 1073
               execv (linker_pathname, linker_argv.args); // execute the linker and produce a stripped procnto (wait for completion)
20 pmbaty 1074
               DIE_WITH_EXITCODE (1, "execve() failed"); // exec never returns
1075
            }
22 pmbaty 1076
            else // we are the parent
20 pmbaty 1077
               waitpid (pid, &status, 0); // wait for the child to finish
1078
         } while (0);
1079
#endif // _WIN32
1080
         STRINGARRAY_FREE (&linker_argv);
16 pmbaty 1081
         if (!Buffer_ReadFromFile (&entry_parms->data, procnto_sym_filename)) // load the output file
15 pmbaty 1082
            DIE_WITH_EXITCODE (1, "the host cross-linker failed to produce a readable stripped \"%s\" kernel: %s", procnto_sym_filename, strerror (errno));
1083
         if (!entry_parms->should_keep_ld_output)
1084
            unlink (procnto_sym_filename); // remove the linker output file if we want to
17 pmbaty 1085
 
20 pmbaty 1086
         // save the boot arguments. The magic to look for is "ddpvbskr" -- whatever that means
1087
         if ((bootargs_location = Buffer_FindFirstByteArray (&entry_parms->data, "ddpvbskr")) == NULL)
1088
            DIE_WITH_EXITCODE (1, "unable to find boot args location in the stripped \"%s\" kernel", stored_pathname);
1089
         Buffer_InitWithSize (&bootargs_buffer, sizeof (bootargs_entry_t)); // prepare a boot args entry
1090
         ((bootargs_entry_t *) bootargs_buffer.bytes)->argc = (uint8_t) procnto_argv.count;
1091
         ((bootargs_entry_t *) bootargs_buffer.bytes)->envc = (uint8_t) (global_envp.count + procnto_envp.count);
1092
         ((bootargs_entry_t *) bootargs_buffer.bytes)->shdr_addr = (uint32_t) (image_base + bootfile_size); // same value as startup_header.image_paddr (which is not set yet) (TODO: support 64-bit shdr_addr offsets -- see comment in bootargs_entry_t struct)
1093
         for (array_index = 0; array_index < procnto_argv.count; array_index++)
1094
            ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, procnto_argv.args[array_index], strlen (procnto_argv.args[array_index]) + 1)); // append string including NUL terminator
1095
         for (array_index = 0; array_index < global_envp.count; array_index++)
1096
            ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, global_envp.args[array_index], strlen (global_envp.args[array_index]) + 1)); // append string including NUL terminator
1097
         for (array_index = 0; array_index < procnto_envp.count; array_index++)
1098
            ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, procnto_envp.args[array_index], strlen (procnto_envp.args[array_index]) + 1)); // append string including NUL terminator
1099
         ((bootargs_entry_t *) bootargs_buffer.bytes)->size_hi = (uint8_t) ((bootargs_buffer.size >> 8) & 0xff);
1100
         ((bootargs_entry_t *) bootargs_buffer.bytes)->size_lo = (uint8_t) ((bootargs_buffer.size >> 0) & 0xff);
1101
         ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (&entry_parms->data, (size_t) bootargs_location - (size_t) entry_parms->data.bytes, &bootargs_buffer));
1102
         Buffer_Forget (&bootargs_buffer); // release the boot args buffer once it's written
1103
 
17 pmbaty 1104
         // now strip this prelinked ELF kernel file
20 pmbaty 1105
         ASSERT_WITH_ERRNO (Buffer_StripELFFile (&entry_parms->data, (const char **) saved_ELF_sections, 1, true, stored_pathname)); // strip the ELF file as per QNX docs (only keep ONE section, which is "QNX_info", and align the segment size in file with the size it occupies in memory)
17 pmbaty 1106
 
20 pmbaty 1107
         sprintf_s (candidate_pathname, MAXPATHLEN, "%s/%s", (entry_parms->prefix != NULL ? entry_parms->prefix : ""), procnto_argv.args[0]); // fix the entry name
1108
         stored_pathname = candidate_pathname;
1109
 
1110
         entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // mark this inode as a preprocessed *bootstrap* ELF file
1111
         entry_parms->st_mode = S_IFREG | entry_parms->perms; // procnto is a regular file
1112
         image_kernel_ino = entry_parms->extra_ino_flags | (inode_count + 1);
1113
 
1114
         STRINGARRAY_FREE (&procnto_argv); // release procnto's argv array
1115
         STRINGARRAY_FREE (&procnto_envp); // release procnto's envp array
22 pmbaty 1116
         //STRINGARRAY_FREE (&global_envp); // DO NOT release the global envp array. It is inherited by the boot scripts.
1117
      } // end of "is bootstrap file"
1118
      else if (entry_parms->is_compiled_bootscript) // else is it a startup script that we need to compile ?
1119
      {
7 pmbaty 1120
         image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
22 pmbaty 1121
         Buffer_Initialize (&compiled_script);
2 pmbaty 1122
 
22 pmbaty 1123
         // parse buffer (non-destructively) line after line
1124
         Buffer_Initialize (&current_line);
1125
         for (line_index = 0; Buffer_GetNthLine (&entry_parms->data, line_index, &current_line); line_index++)
1126
         {
1127
            read_ptr = current_line.bytes;
1128
            while (isspace (*read_ptr))
1129
               read_ptr++; // skip leading spaces
1130
            if ((*read_ptr == '#') || (*read_ptr == 0))
1131
               continue; // skip comments and empty lines
1132
 
1133
            // format of a line: [attributes] [env assignation] [...] [executable] [arg] [...] [&] [comment]
1134
            // example: "[pri=20f] devc-con -n9 &"
1135
 
1136
            LOG_DEBUG ("parsing line: %s", read_ptr);
1137
            Buffer_Initialize (&compiled_scriptline);
1138
            memcpy (&current_scriptcmd_params, &default_scriptcmd_params, sizeof (default_scriptcmd_params));
1139
 
1140
            // does this line start with an attribute block ?
1141
            if (*read_ptr == '[')
1142
            {
1143
               read_ptr++; // skip the leading square bracket
1144
               linebit_start = read_ptr; // remember where it starts
1145
               is_quoted_context = false; // reach the next unescaped closing square bracket that is not between quotes
1146
               while ((*read_ptr != 0) && !((*read_ptr == ']') && (read_ptr[-1] != '\\') && !is_quoted_context))
1147
               {
1148
                  if (*read_ptr == '"')
1149
                     is_quoted_context ^= true; // remember when we're between quotes
1150
                  else if (!is_quoted_context && (*read_ptr == ' '))
1151
                     *read_ptr = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
1152
                  read_ptr++; // reach the next unescaped closing square bracket
1153
               }
1154
               if (*read_ptr != ']')
1155
               {
1156
                  LOG ("warning", 0, "syntax error in \"%s\" line %zd of inline document '%s': unterminated attributes block (skipping)", buildfile_pathname, 1 + line_index, stored_pathname);
1157
                  continue; // invalid attribute block, skip line
1158
               }
1159
               is_end_of_line = (*read_ptr == 0); // see if we're at the end of line already
1160
               *read_ptr = 0; // end the attribute block in all cases so that it is a parsable C string
1161
 
1162
               // now parse the attribute tokens
1163
               token = strtok_r (linebit_start, RECORD_SEP, &ctx);
1164
               while (token != NULL)
1165
               {
1166
                  #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
1167
                  if (false) {}
1168
                  else if (strncmp (token, "argv0=",      6) == 0) { REACH_TOKEN_VALUE (); current_scriptcmd_params.argv0      = value; } // NOTE: stolen pointer. Do not free.
1169
                  else if (strncmp (token, "cpu=",        4) == 0) { REACH_TOKEN_VALUE (); current_scriptcmd_params.cpu_number = (int) atoi (value); }
1170
                  else if (strncmp (token, "pri=",        4) == 0) { REACH_TOKEN_VALUE (); current_scriptcmd_params.priority   = (int) strtol (value, &ctx, 0); if (ctx != NULL) current_scriptcmd_params.sched_policy = (*ctx == 'f' ? SCRIPTCMD_SCHEDULERPOLICY_FIFO : SCRIPTCMD_SCHEDULERPOLICY_RR); }
1171
                  else if (strncmp (token, "sched_aps=", 10) == 0) { REACH_TOKEN_VALUE ();
1172
                     for (array_index = 0; array_index < aps_partnames.count; array_index++) if (strcmp (aps_partnames.args[array_index], value) == 0) break;
1173
                     if (array_index == aps_partnames.count)
1174
                        DIE_WITH_EXITCODE (1, "syntax error in \"%s\" line %zd of inline document '%s': APS partition name '%s' not found: please declare it first", buildfile_pathname, 1 + line_index, stored_pathname, value); // consistency check (TODO: check that the sum of all budgets don't exceed 100%)
1175
                     current_scriptcmd_params.aps_partindex = (int) array_index;
1176
                  }
1177
                  else if (strcmp (token, "+external") == 0) current_scriptcmd_params.is_external = true;
1178
                  else if (strcmp (token, "-external") == 0) current_scriptcmd_params.is_external = false;
1179
                  else if (strcmp (token, "+session")  == 0) current_scriptcmd_params.is_session_leader = true;
1180
                  else if (strcmp (token, "-session")  == 0) current_scriptcmd_params.is_session_leader = false;
1181
                  else if (strcmp (token, "+debug")    == 0) current_scriptcmd_params.has_debug_flag = true;
1182
                  else if (strcmp (token, "-debug")    == 0) current_scriptcmd_params.has_debug_flag = false;
1183
                  else LOG_WARNING ("unimplemented boot script modifier in \"%s\" line %zd of inline document '%s': '%s'", buildfile_pathname, 1 + line_index, stored_pathname, token);
1184
                  #undef REACH_TOKEN_VALUE
1185
                  token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
1186
               }
1187
 
1188
               if (is_end_of_line)
1189
                  continue; // if end of line was reached, proceed to the next line
1190
               else
1191
                  read_ptr++; // else reach the next character (after the NUL split) and continue processing the same line
1192
            } // end of "this line starts with an attributes block"
1193
 
1194
            // at this point we are past the attributes block
1195
 
1196
            // reset contextual argv/envp arrays
1197
            line_argv.args = NULL;
1198
            line_argv.count = 0;
1199
            line_envp.args = NULL;
1200
            line_envp.count = 0;
1201
 
1202
            // now read each word (or quoted group of words), unescaping escaped characters
1203
            while (*read_ptr != 0)
1204
            {
1205
               while ((*read_ptr != 0) && isspace (*read_ptr))
1206
                  read_ptr++; // skip intermediate spaces and reach the next word
1207
 
1208
               if (*read_ptr == '#')
1209
                  break; // if the rest of the line is commented out, stop parsing it and proceed to the next line
1210
 
1211
               linebit_start = read_ptr; // remember the word (or quoted group of words) starts here
1212
               write_ptr = read_ptr;
1213
               is_quoted_context = (*read_ptr == '"'); // see if we're entering a quoted context or not
1214
               if (is_quoted_context)
1215
                  read_ptr++; // skip a possible initial quote in the word
1216
               while ((*read_ptr != 0) && ((!is_quoted_context && !isspace (*read_ptr)) || (is_quoted_context && (*read_ptr != '"'))))
1217
               {
1218
                  if (*read_ptr == '\\')
1219
                     read_ptr++; // unescape characters that are escaped with '\' by advancing the read pointer
1220
                  *write_ptr++ = *read_ptr++; // recopy characters as we read them
1221
               }
1222
               is_end_of_line = (*read_ptr == 0); // see if we're at the end of line already
1223
               *write_ptr = 0; // stop the rewritten string here
1224
 
1225
               // end of word, i.e. we reached either a closing quote or a space. The string bit has been rewritted at linebit_start without quotes and with characters unescaped.
1226
               STRINGARRAY_PUSH (&line_argv, linebit_start);
1227
               LOG_DEBUG ("collected bootscript argv: [%s]", linebit_start);
1228
 
1229
               if (!is_end_of_line)
1230
                  read_ptr++; // if we haven't reach the end of the line yet, advance to the next character (after the NUL split)
1231
            } // end while (*read_ptr != 0)
1232
 
1233
            // we finished parsing the line
1234
 
1235
            // did we fill an executable argv? As per QNX docs, the first executable must be startup-*, the last executable must be procnto.
1236
            if (line_argv.count > 0)
1237
            {
1238
               // is it one of the few builtin commands ?
1239
               if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "waitfor") == 0))
1240
               {
1241
                  if (line_argv.count < 2)
1242
                     DIE_WITH_EXITCODE (1, "syntax error in \"%s\" line %zd of inline document '%s': waitfor requires 1 or 2 arguments", buildfile_pathname, 1 + line_index, stored_pathname);
1243
 
1244
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_WAITFOR "\x00", 4)); // size as u16LE, type, spare
1245
                  wait_time = (line_argv.count > 2 ? (size_t) (10.0 * atof (line_argv.args[2])) : 50); // convert dotted number to tenths of seconds. Default to 5 seconds (50 tenths)
1246
                  if (wait_time > 0xffff)
1247
                     wait_time = 0xffff;
1248
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 4, (wait_time >> 0) & 0xff)); // wait time lo
1249
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 5, (wait_time >> 8) & 0xff)); // wait time hi
1250
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[1], strlen (line_argv.args[1]) + 1));
1251
               }
1252
               else if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "reopen") == 0))
1253
               {
1254
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_REOPEN "\x00", 4)); // size as u16LE, type, spare
1255
                  wait_time = (line_argv.count > 2 ? (size_t) (10.0 * atof (line_argv.args[2])) : 50); // convert dotted number to tenths of seconds. Default to 5 seconds (50 tenths)
1256
                  if (wait_time > 0xffff)
1257
                     wait_time = 0xffff;
1258
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 4, (wait_time >> 0) & 0xff)); // wait time lo
1259
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 5, (wait_time >> 8) & 0xff)); // wait time hi
1260
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, (line_argv.count > 1 ? line_argv.args[1] : "/dev/console"), strlen (line_argv.count > 1 ? line_argv.args[1] : "/dev/console") + 1));
1261
               }
1262
               else if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "display_msg") == 0))
1263
               {
1264
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_DISPLAY_MSG "\x00", 4)); // size as u16LE, type, spare
1265
                  for (array_index = 1; array_index < line_argv.count; array_index++)
1266
                  {
1267
                     if (array_index > 1)
1268
                        ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_scriptline, " ")); // separate each arg with a space
1269
                     ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[array_index], strlen (line_argv.args[array_index])));
1270
                  }
1271
                  ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_scriptline, "\n\0")); // don't forget to append a newline to the message printed
1272
               }
1273
               else if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "procmgr_symlink") == 0))
1274
               {
1275
                  if (line_argv.count < 3)
1276
                     DIE_WITH_EXITCODE (1, "syntax error in \"%s\" line %zd of inline document '%s': procmgr_symlink requires 2 arguments", buildfile_pathname, 1 + line_index, stored_pathname);
1277
 
1278
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_PROCMGR_SYMLINK "\x00", 4)); // size as u16LE, type, spare
1279
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[1], strlen (line_argv.args[1]) + 1));
1280
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[2], strlen (line_argv.args[2]) + 1));
1281
               }
1282
               else if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "sched_aps") == 0))
1283
               {
1284
                  token = (line_argv.count > 1 ? line_argv.args[1] : "System");
1285
                  if ((strlen (token) > 15) || (strchr (token, '/') != NULL))
1286
                     DIE_WITH_EXITCODE (1, "syntax error in \"%s\" line %zd of inline document '%s': APS partition names must be less than 16 characters long and not contain a '/' separator", buildfile_pathname, 1 + line_index, stored_pathname); // consistency check (TODO: check that the sum of all budgets don't exceed 100%)
1287
                  for (array_index = 0; array_index < aps_partnames.count; array_index++)
1288
                     if (strcmp (aps_partnames.args[array_index], token) == 0)
1289
                        break; // find the APS partition ID in the global APS partition names table
1290
                  if (array_index == aps_partnames.count)
1291
                     STRINGARRAY_PUSH (&aps_partnames, token); // if not found, add a new partition name to the table
1292
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_EXTSCHED_APS "\x00", 4)); // size as u16LE, type, spare
1293
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 4, 0)); // parent (system partition)
1294
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 5, (line_argv.count > 2 ? (uint8_t) atoi (line_argv.args[2]) : 0))); // budget
1295
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 6, ((line_argv.count > 3 ? (uint8_t) atoi (line_argv.args[3]) : 0) >> 0) & 0xff)); // critical lo
1296
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 7, ((line_argv.count > 3 ? (uint8_t) atoi (line_argv.args[3]) : 0) >> 8) & 0xff)); // critical hi
1297
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 8, (uint8_t) array_index)); // APS partition ID
1298
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, token, strlen (token) + 1)); // partition name
1299
               }
1300
               else // not a builtin, which means it is an external command
1301
               {
1302
                  if (strcmp (line_argv.args[line_argv.count - 1], "&") == 0) // is the last argument an ampersand (fork sign) on its own ? (variant 1)
1303
                  {
1304
                     current_scriptcmd_params.is_background_task = true; // remember this is a background task
1305
                     free (line_argv.args[line_argv.count - 1]); // prevent leaking the last arg
1306
                     line_argv.count--; // and adjust the arg count
1307
                  }
1308
                  else if (((token = strrchr (line_argv.args[line_argv.count - 1], '&')) != NULL) && (token[1] == 0)) // else does the last argument END with a fork sign ? (variant 2)
1309
                  {
1310
                     current_scriptcmd_params.is_background_task = true; // remember this is a background task
1311
                     *token = 0; // and chop off the ampersand from that arg
1312
                  }
1313
 
1314
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_EXTERNAL "\x00", 4)); // size as u16LE, type, spare
1315
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 4, (current_scriptcmd_params.cpu_number != -1 ? (uint8_t) current_scriptcmd_params.cpu_number : 0))); // CPU
1316
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 5, (current_scriptcmd_params.aps_partindex != -1 ? SCRIPTCMD_FLAG_EXTSCHED   : 0)
1317
                                                                                | (current_scriptcmd_params.is_session_leader   ? SCRIPTCMD_FLAG_SESSION    : 0)
1318
                                                                                | (current_scriptcmd_params.sched_policy  != -1 ? SCRIPTCMD_FLAG_SCHED_SET  : 0)
1319
                                                                                | (current_scriptcmd_params.cpu_number    != -1 ? SCRIPTCMD_FLAG_CPU_SET    : 0)
1320
                                                                                | (current_scriptcmd_params.is_background_task  ? SCRIPTCMD_FLAG_BACKGROUND : 0)
1321
                                                                                | (current_scriptcmd_params.has_debug_flag      ? SCRIPTCMD_FLAG_KDEBUG     : 0))); // flags
1322
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 6, (current_scriptcmd_params.aps_partindex != -1 ? (uint8_t) current_scriptcmd_params.aps_partindex : 0))); // adaptative partitioning ID
1323
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 7, 0)); // reserved
1324
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 8, (current_scriptcmd_params.sched_policy != -1 ? current_scriptcmd_params.sched_policy : 0))); // scheduling policy
1325
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 9, (current_scriptcmd_params.priority != -1 ? current_scriptcmd_params.priority : 0))); // scheduling priority
1326
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 10, (uint8_t) line_argv.count)); // argc
1327
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 11, (uint8_t) global_envp.count)); // envc
1328
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[0], strlen (line_argv.args[0]) + 1)); // executable
1329
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, (current_scriptcmd_params.argv0 != NULL ? current_scriptcmd_params.argv0 : line_argv.args[0]), strlen (current_scriptcmd_params.argv0 != NULL ? current_scriptcmd_params.argv0 : line_argv.args[0]) + 1)); // argv[0]
1330
                  for (array_index = 1; array_index < line_argv.count; array_index++)
1331
                     ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[array_index], strlen (line_argv.args[array_index]) + 1)); // argv[n]
1332
                  for (array_index = 0; array_index < global_envp.count; array_index++)
1333
                     ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, global_envp.args[array_index], strlen (global_envp.args[array_index]) + 1)); // envp[n]
1334
               }
1335
               ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&compiled_scriptline, ROUND_TO_UPPER_MULTIPLE (compiled_scriptline.size, 4))); // pad compiled command buffer to upper 32-bit multiple
1336
 
1337
               // fix the size of this compiled boot script command
1338
               ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 0, (compiled_scriptline.size >> 0) & 0xff)); // size lo
1339
               ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 1, (compiled_scriptline.size >> 8) & 0xff)); // size hi
1340
 
1341
               // now concatenate this newly compiled boot script line to the compiled boot script buffer
1342
               ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&compiled_script, &compiled_scriptline));
1343
               Buffer_Forget (&compiled_scriptline);
1344
            }
1345
            else // this line contained no executable invokation, so make the parameters that changed the default ones
1346
            {
1347
               #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (current_scriptcmd_params.attr != default_scriptcmd_params.attr) { \
1348
                     LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %zd of inline document '%s'", default_scriptcmd_params.attr, current_scriptcmd_params.attr, buildfile_pathname, 1 + line_index, stored_pathname); \
1349
                     default_scriptcmd_params.attr = current_scriptcmd_params.attr; \
1350
                  } } while (0)
1351
               #define APPLY_DEFAULT_ATTR_STR(attr,descr,fmt) do { if (((default_scriptcmd_params.attr == NULL) && (current_scriptcmd_params.attr != NULL)) || ((default_scriptcmd_params.attr != NULL) && (current_scriptcmd_params.attr == NULL)) || ((default_scriptcmd_params.attr != NULL) && (current_scriptcmd_params.attr != NULL) && (strcmp (current_scriptcmd_params.attr, default_scriptcmd_params.attr) != 0))) { \
1352
                  LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %zd of inline document '%s'", (default_scriptcmd_params.attr != NULL ? default_scriptcmd_params.attr : "none"), current_scriptcmd_params.attr, buildfile_pathname, 1 + line_index, stored_pathname); \
1353
                     default_scriptcmd_params.attr = current_scriptcmd_params.attr; \
1354
                  } } while (0)
1355
               APPLY_DEFAULT_ATTR_STR (argv0,                          "executable name",                 "\"%s\"");
1356
               APPLY_DEFAULT_ATTR_NUM (cpu_number,                     "CPU mask",                        "0%o");
1357
               APPLY_DEFAULT_ATTR_NUM (is_external,                    "external command flag",           "0%o");
1358
               APPLY_DEFAULT_ATTR_NUM (priority,                       "scheduling priority",             "0%o");
1359
               APPLY_DEFAULT_ATTR_NUM (sched_policy,                   "scheduling policy",               "0%o");
1360
               APPLY_DEFAULT_ATTR_NUM (aps_partindex,                  "APS partition index",             "0%o");
1361
               APPLY_DEFAULT_ATTR_NUM (is_session_leader,              "session leader flag",             "0%o");
1362
               APPLY_DEFAULT_ATTR_NUM (is_background_task,             "background task flag",            "0%o");
1363
               APPLY_DEFAULT_ATTR_NUM (has_debug_flag,                 "debug flag",                      "0%o");
1364
               #undef APPLY_DEFAULT_ATTR_STR
1365
               #undef APPLY_DEFAULT_ATTR_NUM
1366
            }
1367
 
1368
            // release the contextual argv/envp arrays
1369
            STRINGARRAY_FREE (&line_argv);
1370
            STRINGARRAY_FREE (&line_envp);
1371
 
1372
         } // end for (line_index = 0; Buffer_GetNthLine (&entry_parms->data, line_index, &current_line); line_index++)
1373
         Buffer_Forget (&entry_parms->data); // free the inline specification once it's parsed
1374
 
1375
         ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_script, "\x00\x00\x00\x00")); // terminate the compiled boot script with a 4-byte trailer
1376
         entry_parms->data.bytes = compiled_script.bytes; // and steal the compiled boot script buffer
1377
         entry_parms->data.size = compiled_script.size;
1378
      } // end of "is compiled bootscript"
1379
 
7 pmbaty 1380
      // do we already know the data for this data blob ?
11 pmbaty 1381
      if (entry_parms->data.bytes != NULL)
7 pmbaty 1382
      {
15 pmbaty 1383
         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 1384
         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 1385
      }
8 pmbaty 1386
      else if (buildhost_pathname != NULL) // else was a source file pathname supplied ?
1 pmbaty 1387
      {
17 pmbaty 1388
         resolved_pathname = resolve_pathname (buildhost_pathname, entry_parms->search); // locate the file
7 pmbaty 1389
         if (resolved_pathname == NULL)
18 pmbaty 1390
         {
1391
            if (entry_parms->should_allow_nonexistent_files)
1392
            {
1393
               LOG_WARNING ("filesystem entry \"%s\" specified in \"%s\" line %d not found on build host: ignoring", buildhost_pathname, buildfile_pathname, lineno);
19 pmbaty 1394
               return; // if we're allowed to continue when a file to add doesn't exist, do so, else die with an error message
18 pmbaty 1395
            }
15 pmbaty 1396
            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));
18 pmbaty 1397
         }
16 pmbaty 1398
         if (!Buffer_ReadFromFile (&entry_parms->data, resolved_pathname))
1399
            DIE_WITH_EXITCODE (1, "filesystem entry \"%s\" specified in \"%s\" line %d can't be read: %s", buildhost_pathname, buildfile_pathname, lineno, strerror (errno));
1400
         stat (resolved_pathname, &stat_buf); // can't fail, since we could read it
7 pmbaty 1401
         if (entry_parms->mtime == UINT32_MAX)
1402
            entry_parms->mtime = (uint32_t) stat_buf.st_mtime;
16 pmbaty 1403
         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 1404
      }
19 pmbaty 1405
      else
1406
         DIE_WITH_EXITCODE (1, "unexpected code path: can't store a file without neither explicit contents nor a host pathname. This is a bug in the program. Please contact the author.");
1 pmbaty 1407
 
9 pmbaty 1408
      // is the file we're storing an ELF file ?
16 pmbaty 1409
      #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
1410
      if ((entry_parms->data.size > 52) // file is big enough to contain an ELF header
1411
          && (memcmp (ELF_GET_STRING (ELFHDR, ELFHDR, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic
7 pmbaty 1412
      {
9 pmbaty 1413
         // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ?
16 pmbaty 1414
         if ((ELF_GET_NUMERIC (ELFHDR, ELFHDR, type) == ELF_TYPE_DYNAMICLIB) && entry_parms->should_autosymlink_dylib)
7 pmbaty 1415
         {
9 pmbaty 1416
            // locate the sections we need (the dynamic section and its strings table)
16 pmbaty 1417
            const elf_section_header_t *shdr_dynamic = elf_get_section_header_by_name (ELFHDR, ".dynamic");
1418
            const elf_section_header_t *shdr_dynstr = elf_get_section_header_by_name (ELFHDR, ".dynstr");
7 pmbaty 1419
 
9 pmbaty 1420
            // make sure we have both the dynamic section header and its own strings table header
1421
            if ((shdr_dynamic != NULL) && (shdr_dynstr != NULL))
1422
            {
16 pmbaty 1423
               dynamic_strings = (char *) &entry_parms->data.bytes[ELF_GET_NUMERIC (ELFHDR, shdr_dynstr, file_offset)]; // quick access to dynamic sections strings table
7 pmbaty 1424
 
9 pmbaty 1425
               // walk through the dynamic section, look for the DT_SONAME entry
16 pmbaty 1426
               canonical_dylib_name = NULL; // assume none until told otherwise
1427
               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)];
1428
                    (ELF_GET_NUMERIC (ELFHDR, dynamic_entry, tag) != ELF_DT_NULL);
1429
                    dynamic_entry = (elf_dynamic_section_entry_t *) ((uint8_t *) dynamic_entry + ELF_STRUCT_SIZE (ELFHDR, dynamic_entry)))
1430
                  if (ELF_GET_NUMERIC (ELFHDR, dynamic_entry, tag) == ELF_DT_SONAME)
9 pmbaty 1431
                  {
16 pmbaty 1432
                     canonical_dylib_name = dynamic_strings + ELF_GET_NUMERIC (ELFHDR, dynamic_entry, value);
9 pmbaty 1433
                     break;
1434
                  }
1435
 
1436
               // do we have it ?
1437
               if ((canonical_dylib_name != NULL) && (canonical_dylib_name[0] != 0))
7 pmbaty 1438
               {
17 pmbaty 1439
                  sprintf_s (candidate_pathname, MAXPATHLEN, "%s/%s", (entry_parms->prefix != NULL ? entry_parms->prefix : ""), canonical_dylib_name);
9 pmbaty 1440
                  if (strcmp (candidate_pathname, stored_pathname) != 0) // claimed dylib name differs from passed name ?
1441
                  {
1442
                     original_stored_pathname = stored_pathname; // if so, remember to create a symlink here
1443
                     stored_pathname = candidate_pathname;
1444
                  }
7 pmbaty 1445
               }
1 pmbaty 1446
            }
9 pmbaty 1447
         } // end if the file we're storing is a dylib
10 pmbaty 1448
 
1449
         // now strip this ELF file if necessary
1450
         if (!(entry_parms->extra_ino_flags & IFS_INO_PROCESSED_ELF))
1451
         {
20 pmbaty 1452
            Buffer_StripELFFile (&entry_parms->data, (const char **) saved_ELF_sections, saved_ELF_section_count, false, stored_pathname); // strip the ELF file à la mkifs
10 pmbaty 1453
            entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF; // mark this inode as a preprocessed ELF file
1454
         } // end if the file is not yet a processed ELF
9 pmbaty 1455
      } // end if the file we're storing is an ELF file
16 pmbaty 1456
      #undef ELFHDR // undefine the macro that used to always point to the ELF header at the beginning of the file
1 pmbaty 1457
   }
1458
 
18 pmbaty 1459
   // have a pointer to where the stored pathname actually starts, without the leading slash
1460
   stored_pathname_without_leading_slash = stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname;
1461
 
1462
   // see if this item already has an entry in the current list of filesystem entries
1463
   for (fsentry_index = 0; fsentry_index < *fsentry_count; fsentry_index++)
1464
   {
1465
      fsentry = &(*fsentries)[fsentry_index]; // quick access to fs entry slot
1466
      if (   (S_ISDIR  (fsentry->header.mode) && (strcmp (fsentry->u.dir.path,     stored_pathname_without_leading_slash) == 0))
19 pmbaty 1467
          || (S_ISREG  (fsentry->header.mode) && (strcmp (fsentry->u.file.path,    stored_pathname_without_leading_slash) == 0))
1468
          || (S_ISLNK  (fsentry->header.mode) && (strcmp (fsentry->u.symlink.path, stored_pathname_without_leading_slash) == 0))
1469
          || (S_ISFIFO (fsentry->header.mode) && (strcmp (fsentry->u.symlink.path, stored_pathname_without_leading_slash) == 0)))
18 pmbaty 1470
         break; // stop searching as soon as we find a duplicate
1471
   }
1472
 
1473
   // is there already an entry for this item ?
1474
   if (fsentry_index < *fsentry_count)
1475
   {
1476
      // if we should NOT ignore duplicates, bomb out, else just reuse that entry
1477
      if (!entry_parms->should_ignore_duplicates)
1478
         DIE_WITH_EXITCODE (1, "duplicate detected: entry \"%s\" specified in \"%s\" line %d already exists in IFS file", stored_pathname, buildfile_pathname, lineno);
1479
   }
1480
   else // this is a new entry: grow filesystem entries array to hold one more slot
1481
   {
1482
      reallocated_ptr = realloc (*fsentries, (*fsentry_count + 1) * sizeof (fsentry_t)); // attempt to reallocate
1483
      ASSERT_WITH_ERRNO (reallocated_ptr); // verify
1484
      *fsentries = reallocated_ptr; // save reallocated pointer
1485
      fsentry = &(*fsentries)[*fsentry_count]; // quick access to fs entry slot
1486
      (*fsentry_count)++; // remember there's one entry more in the array
1487
   }
1488
 
1489
   // save (or update) this entry's parameters
1 pmbaty 1490
   fsentry->header.extattr_offset = 0;
10 pmbaty 1491
   fsentry->header.ino = entry_parms->extra_ino_flags | (++inode_count);
1 pmbaty 1492
   fsentry->header.mode = entry_parms->st_mode;
1493
   fsentry->header.gid = entry_parms->gid;
1494
   fsentry->header.uid = entry_parms->uid;
7 pmbaty 1495
   fsentry->header.mtime = (entry_parms->mtime == UINT32_MAX ? (uint32_t) time (NULL) : entry_parms->mtime);
1 pmbaty 1496
   if (S_ISDIR (entry_parms->st_mode))
1497
   {
18 pmbaty 1498
      fsentry->u.dir.path = strdup (stored_pathname_without_leading_slash);
16 pmbaty 1499
 
7 pmbaty 1500
      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 1501
      fsentry->UNSAVED_was_data_written = true; // no data to save
1502
   }
1503
   else if (S_ISREG (entry_parms->st_mode))
1504
   {
1505
      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 1506
      fsentry->u.file.size = (uint32_t) entry_parms->data.size;
18 pmbaty 1507
      fsentry->u.file.path = strdup (stored_pathname_without_leading_slash);
16 pmbaty 1508
      fsentry->u.file.UNSAVED_databuf = malloc (entry_parms->data.size);
1509
      ASSERT_WITH_ERRNO (fsentry->u.file.UNSAVED_databuf);
1510
      memcpy (fsentry->u.file.UNSAVED_databuf, entry_parms->data.bytes, entry_parms->data.size);
1511
 
7 pmbaty 1512
      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 1513
      fsentry->UNSAVED_was_data_written = false; // there *IS* data to save
1514
   }
1515
   else if (S_ISLNK (entry_parms->st_mode))
1516
   {
18 pmbaty 1517
      fsentry->u.symlink.sym_offset = (uint16_t) (strlen (stored_pathname_without_leading_slash) + 1);
16 pmbaty 1518
      fsentry->u.symlink.sym_size = (uint16_t) entry_parms->data.size;
18 pmbaty 1519
      fsentry->u.symlink.path = strdup (stored_pathname_without_leading_slash);
11 pmbaty 1520
      fsentry->u.symlink.contents = strdup (entry_parms->data.bytes);
16 pmbaty 1521
      ASSERT_WITH_ERRNO (fsentry->u.symlink.contents);
1522
 
7 pmbaty 1523
      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 1524
      fsentry->UNSAVED_was_data_written = true; // no data to save
1525
   }
7 pmbaty 1526
   else // necessarily a device node
1 pmbaty 1527
   {
11 pmbaty 1528
      fsentry->u.device.dev  = strtol (entry_parms->data.bytes, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
1529
      fsentry->u.device.rdev = strtol (strchr (entry_parms->data.bytes, ':') + 1, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
18 pmbaty 1530
      fsentry->u.device.path = strdup (stored_pathname_without_leading_slash);
16 pmbaty 1531
 
7 pmbaty 1532
      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 1533
      fsentry->UNSAVED_was_data_written = true; // no data to save
1534
   }
7 pmbaty 1535
 
1536
   // should we also add a symlink to this entry ? (in case we stored a dylib file under its canonical name)
1537
   if (original_stored_pathname != NULL)
1538
   {
1539
      entry_parms->is_compiled_bootscript = false;
1540
      entry_parms->should_autosymlink_dylib = false;
1541
      entry_parms->should_follow_symlinks = false;
1542
      entry_parms->st_mode = S_IFLNK | 0777; // NOTE: mkifs stores symlink permissions as rwxrwxrwx !
10 pmbaty 1543
      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 1544
      last_dirsep = strrchr (stored_pathname, '/');
11 pmbaty 1545
      old_data = entry_parms->data.bytes; // backup previous data pointer
1546
      entry_parms->data.bytes = (uint8_t *) (last_dirsep == NULL ? stored_pathname : last_dirsep + 1); // store symlink target in dirent data
16 pmbaty 1547
      entry_parms->data.size = strlen (entry_parms->data.bytes);
7 pmbaty 1548
      add_fsentry (fsentries, fsentry_count, entry_parms, original_stored_pathname, NULL);
11 pmbaty 1549
      entry_parms->data.bytes = old_data; // restore previous data pointer so that it can be freed normally
7 pmbaty 1550
   }
1551
 
19 pmbaty 1552
   return; // finished, return to our caller
1 pmbaty 1553
}
1554
 
1555
 
19 pmbaty 1556
static void add_directory_contents_recursively (fsentry_t **fsentries, size_t *fsentry_count, const char *dir_pathname, const size_t start_pathname_len, parms_t *default_parms)
1557
{
1558
   // adds the contents of the directory pointed to by dir_pathname to the fsentries array, recursively
1559
   // start_pathname_len is initialized to the length of dir_pathname by the top caller, and passed down unchanged,
1560
   // so that each sublevel of the recursion knows the depth of the relative path in which it is.
1561
 
1562
   thread_local static char item_pathname[MAXPATHLEN] = "";
1563
   thread_local static parms_t entry_parms = { 0 };
1564
   thread_local static struct stat stat_buf = { 0 };
1565
   thread_local static char major_minor[64];
1566
 
1567
   DIR *dirp;
1568
   struct dirent *dp;
1569
 
1570
   // open the directory
1571
   dirp = opendir (dir_pathname);
1572
   if (dirp == NULL)
1573
      DIE_WITH_EXITCODE (1, "unable to open directory \"%s\" for recursive inclusion", dir_pathname);
1574
 
1575
   // enumerate its contents
1576
   while ((dp = readdir (dirp)) != NULL)
1577
   {
1578
      if ((strcmp (dp->d_name, ".") == 0) || (strcmp (dp->d_name, "..") == 0))
1579
         continue; // skip self and parent
1580
 
1581
      memcpy (&entry_parms, default_parms, sizeof (parms_t));
1582
      sprintf_s (item_pathname, sizeof (item_pathname), "%s/%s", dir_pathname, dp->d_name); // construct item's pathname
1583
      ASSERT_WITH_ERRNO (stat (item_pathname, &stat_buf) == 0); // peek info about this entry (or die trying)
1584
      if (S_ISDIR (stat_buf.st_mode))
1585
      {
1586
         entry_parms.st_mode |= entry_parms.dperms; // apply DIRECTORY default permissions
1587
         add_fsentry (fsentries, fsentry_count, &entry_parms, &item_pathname[start_pathname_len], NULL); // add a filesystem entry of type "directory"
1588
         add_directory_contents_recursively (fsentries, fsentry_count, item_pathname, start_pathname_len, default_parms); // dwell into this directory and add its children recursively
1589
      }
1590
      else if (S_ISLNK (stat_buf.st_mode))
1591
      {
1592
         entry_parms.st_mode |= 0777; // NOTE: mkifs sets symlink permissions to rwxrwxrwx !?
1593
         add_fsentry (fsentries, fsentry_count, &entry_parms, &item_pathname[start_pathname_len], item_pathname); // add a filesystem entry of type "link"
1594
      }
1595
      else if (S_ISREG (stat_buf.st_mode))
1596
      {
1597
         entry_parms.st_mode |= entry_parms.perms; // apply FILE default permissions
1598
         add_fsentry (fsentries, fsentry_count, &entry_parms, &item_pathname[start_pathname_len], item_pathname); // add a filesystem entry of type "regular file"
1599
      }
1600
      else if (S_ISFIFO (stat_buf.st_mode))
1601
      {
1602
         entry_parms.st_mode |= entry_parms.perms; // apply FILE default permissions
1603
         sprintf_s (major_minor, sizeof (major_minor), "%u:%u", (unsigned int) major (stat_buf.st_rdev), (unsigned int) minor (stat_buf.st_rdev));
1604
         entry_parms.data.bytes = major_minor;
1605
         add_fsentry (fsentries, fsentry_count, &entry_parms, &item_pathname[start_pathname_len], NULL); // add a filesystem entry of type "FIFO"
1606
      }
1607
      else
1608
         LOG_WARNING ("ignoring unsupported directory entry: \"%s\" (type 0%o)", item_pathname, stat_buf.st_mode & S_IFMT);
1609
   }
1610
 
1611
   closedir (dirp); // finished parsing this level, close the directory handle
1612
   return; // and return to our caller
1613
}
1614
 
1615
 
1 pmbaty 1616
static int fsentry_compare_pathnames_cb (const void *a, const void *b)
1617
{
1618
   // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname
1619
 
3 pmbaty 1620
   const fsentry_t *entry_a = (const fsentry_t *) a;
1621
   const fsentry_t *entry_b = (const fsentry_t *) b;
1 pmbaty 1622
   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)));
1623
   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)));
1624
   return (strcmp (pathname_a, pathname_b));
1625
}
1626
 
1627
 
17 pmbaty 1628
static void parse_line (FILE *buildfile_fp, char *line_buffer, fsentry_t **fsentries, size_t *fsentry_count, parms_t *default_parms)
1 pmbaty 1629
{
17 pmbaty 1630
   thread_local static char path_on_buildhost[MAXPATHLEN] = "";
1631
   thread_local static char path_in_ifs[MAXPATHLEN] = "";
1632
   thread_local 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 1633
 
17 pmbaty 1634
   bool should_discard_inline_contents;
1635
   bool is_quoted_context;
1636
   bool is_escaped_char;
1637
   struct stat stat_buf;
1638
   struct tm utc_time;
1639
   void *reallocated_ptr;
1640
   size_t allocated_size;
1641
   size_t string_len;
1642
   char *specifiedpathname_start;
1643
   char *attrblock_start;
1644
   char *write_ptr;
1645
   char *line_ptr;
1646
   char *value;
7 pmbaty 1647
   char *token;
17 pmbaty 1648
   char *sep;
1649
   char *ctx;
1650
   int read_char;
1651
 
1652
   line_ptr = line_buffer;
1653
   while ((*line_ptr != 0) && isspace (*line_ptr))
1654
      line_ptr++; // skip leading spaces
1655
 
1656
   if ((*line_ptr == 0) || (*line_ptr == '#'))
19 pmbaty 1657
      return; // don't process empty lines and comments
17 pmbaty 1658
 
1659
   string_len = (int) strlen (line_buffer);
1660
   if ((string_len > 0) && (line_buffer[string_len - 1] == '\n'))
1661
      line_buffer[string_len - 1] = 0; // chop off newline for easier debug output
1662
 
1663
   // reset entry values
1664
   memcpy (&entry_parms, default_parms, sizeof (parms_t));
1665
   path_in_ifs[0] = 0;
1666
   path_on_buildhost[0] = 0;
1667
   should_discard_inline_contents = false;
1668
 
1669
   // does this line start with an attribute block ?
1670
   if (*line_ptr == '[')
7 pmbaty 1671
   {
17 pmbaty 1672
      line_ptr++; // skip the leading square bracket
1673
      attrblock_start = line_ptr; // remember where it starts
1674
      is_quoted_context = false;
1675
      while ((*line_ptr != 0) && !((*line_ptr == ']') && (line_ptr[-1] != '\\') && !is_quoted_context))
1676
      {
1677
         if (*line_ptr == '"')
1678
            is_quoted_context ^= true; // remember when we're between quotes
1679
         else if (!is_quoted_context && (*line_ptr == ' '))
1680
            *line_ptr = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
1681
         line_ptr++; // reach the next unescaped closing square bracket
1682
      }
1683
      if (*line_ptr != ']')
1684
      {
1685
         LOG ("warning", 0, "syntax error in \"%s\" line %d: unterminated attributes block (skipping)", buildfile_pathname, lineno);
1686
         return; // invalid attribute block, skip line
1687
      }
1688
      *line_ptr = 0; // end the attribute block so that it is a parsable C string
7 pmbaty 1689
 
17 pmbaty 1690
      // now parse the attribute tokens
1691
      // DOCUMENTATION: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/m/mkifs.html#mkifs__description
1692
      token = strtok_r (attrblock_start, RECORD_SEP, &ctx);
1693
      while (token != NULL)
1694
      {
1695
         // evaluate attribute token
1696
         #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
1697
         if (false) {}
1698
         else if (strncmp (token, "prefix=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms.prefix =  (*value == '/' ? value + 1 : value); } // skip possible leading slash in prefix (NOTE: stolen pointer. Do not free.)
1699
         else if (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.uid     = (int) read_integer (value); }
1700
         else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.gid     = (int) read_integer (value); }
1701
         else if (strncmp (token, "dperms=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms.dperms  = (int) read_integer (value); }
1702
         else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms.perms   = (int) read_integer (value); }
1703
         else if (strncmp (token, "type=",    5) == 0) { REACH_TOKEN_VALUE ();
1704
            if      (strcmp (value, "dir")  == 0) entry_parms.st_mode = S_IFDIR;
1705
            else if (strcmp (value, "file") == 0) entry_parms.st_mode = S_IFREG;
1706
            else if (strcmp (value, "link") == 0) entry_parms.st_mode = S_IFLNK;
1707
            else if (strcmp (value, "fifo") == 0) entry_parms.st_mode = S_IFIFO;
1708
            else DIE_WITH_EXITCODE (1, "invalid 'type' attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, value);
1709
         }
1710
         else if (strncmp (token, "image=",   6) == 0) { REACH_TOKEN_VALUE ();
1711
            image_base = (uint32_t) read_integer (value); // read image base address
1712
            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.)
1713
            if ((sep = strchr (value, ',')) != NULL) image_maxsize   = (uint32_t) read_integer (sep + 1); // if we have a comma, read optional image max size
1714
            if ((sep = strchr (value, '=')) != NULL) image_totalsize = (uint32_t) read_integer (sep + 1); // if we have an equal sign, read optional image padding size
1715
            if ((sep = strchr (value, '%')) != NULL) image_align     = (uint32_t) read_integer (sep + 1); // if we have a modulo sign, read optional image aligmnent
1716
            LOG_INFO ("image 0x%x-0x%x maxsize %d totalsize %d align %d", image_base, image_end, image_maxsize, image_totalsize, image_align);
1717
         }
1718
         else if (strncmp (token, "virtual=", 8) == 0) { REACH_TOKEN_VALUE ();
22 pmbaty 1719
            if ((bootfile_pathname == NULL) || (startupfile_pathname == NULL)) // FIXME: HACK until I figure out how to re-create them
1720
               DIE_WITH_EXITCODE (1, "creating bootable images require the --bootfile and --startupfile command-line options in \"%s\" line %d", buildfile_pathname, lineno);
17 pmbaty 1721
            if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ?
1722
            {
1723
               *sep = 0;
1724
               strcpy_s (image_processor, sizeof (image_processor), value); // save processor
1725
               value = sep + 1;
1726
            }
1727
            if (stat (bootfile_pathname, &stat_buf) != 0)
1728
               DIE_WITH_EXITCODE (1, "unable to stat the boot file \"%s\" specified in \"%s\" line %d: %s", bootfile_pathname, buildfile_pathname, lineno, strerror (errno));
1729
            bootfile_size = stat_buf.st_size; // save preboot file size
1730
            LOG_INFO ("processor \"%s\" bootfile \"%s\"\n", image_processor, bootfile_pathname);
20 pmbaty 1731
            entry_parms.is_bootstrap_file = true;
17 pmbaty 1732
         }
1733
         else if (strncmp (token, "mtime=", 6) == 0) { REACH_TOKEN_VALUE (); if (strcmp (value, "*") == 0) entry_parms.mtime = UINT32_MAX; else {
1734
               // value *must* be "YYYY-MM-DD-HH:MM:SS" by specification
1735
               memset (&utc_time, 0, sizeof (utc_time));
1736
               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)
1737
               {
1738
                  LOG_WARNING ("syntax error in \"%s\" line %d: mtime specification not in YYYY-MM-DD-HH:MM:SS format (skipping)", buildfile_pathname, lineno);
1739
                  continue; // invalid attribute block, skip line
1740
               }
1741
               utc_time.tm_mon--; // convert month from [1-12] to [0-11]
1742
               entry_parms.mtime = (uint32_t) mktime (&utc_time);
1743
            }
1744
         }
22 pmbaty 1745
         else if (strcmp (token, "+script")     == 0) entry_parms.is_compiled_bootscript         = true;
19 pmbaty 1746
         else if (strcmp (token, "-script")     == 0) entry_parms.is_compiled_bootscript         = false;
1747
         else if (strcmp (token, "+followlink") == 0) entry_parms.should_follow_symlinks         = true;
1748
         else if (strcmp (token, "-followlink") == 0) entry_parms.should_follow_symlinks         = false;
1749
         else if (strcmp (token, "+autolink")   == 0) entry_parms.should_autosymlink_dylib       = true;
1750
         else if (strcmp (token, "-autolink")   == 0) entry_parms.should_autosymlink_dylib       = false;
1751
         else if (strcmp (token, "+keeplinked") == 0) entry_parms.should_keep_ld_output          = true;
1752
         else if (strcmp (token, "-keeplinked") == 0) entry_parms.should_keep_ld_output          = false;
1753
         else if (strcmp (token, "+dupignore")  == 0) entry_parms.should_ignore_duplicates       = true;
1754
         else if (strcmp (token, "-dupignore")  == 0) entry_parms.should_ignore_duplicates       = false;
18 pmbaty 1755
         else if (strcmp (token, "+optional")   == 0) entry_parms.should_allow_nonexistent_files = true;
1756
         else if (strcmp (token, "-optional")   == 0) entry_parms.should_allow_nonexistent_files = false;
17 pmbaty 1757
         else LOG_WARNING ("unimplemented attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, token);
1758
         #undef REACH_TOKEN_VALUE
7 pmbaty 1759
 
17 pmbaty 1760
         token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
1761
      }
1762
 
1763
      line_ptr++; // reach the next character
1764
      while ((*line_ptr != 0) && isspace (*line_ptr))
1765
         line_ptr++; // skip leading spaces
1766
 
1767
      // are we at the end of the line ? if so, it means the attribute values that are set should become the default
1768
      if ((*line_ptr == 0) || (*line_ptr == '#'))
1769
      {
1770
         #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (entry_parms.attr != default_parms->attr) { \
1771
               LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d", default_parms->attr, entry_parms.attr, buildfile_pathname, lineno); \
1772
               default_parms->attr = entry_parms.attr; \
1773
            } } while (0)
1774
         #define APPLY_DEFAULT_ATTR_STR(attr,descr,fmt) do { if (((default_parms->attr == NULL) && (entry_parms.attr != NULL)) || ((default_parms->attr != NULL) && (entry_parms.attr == NULL)) || ((default_parms->attr != NULL) && (entry_parms.attr != NULL) && (strcmp (entry_parms.attr, default_parms->attr) != 0))) { \
1775
            LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d", (default_parms->attr != NULL ? default_parms->attr : "none"), entry_parms.attr, buildfile_pathname, lineno); \
1776
               default_parms->attr = entry_parms.attr; \
1777
            } } while (0)
18 pmbaty 1778
         //APPLY_DEFAULT_ATTR_STR (new_cwd,                        "current working directory",       "\"%s\"");
1779
         APPLY_DEFAULT_ATTR_STR (search,                         "search path list",                "\"%s\"");
1780
         APPLY_DEFAULT_ATTR_STR (prefix,                         "prefix",                          "\"%s\"");
1781
         APPLY_DEFAULT_ATTR_NUM (dperms,                         "directory permissions",           "0%o");
1782
         APPLY_DEFAULT_ATTR_NUM (perms,                          "file permissions",                "0%o");
1783
         APPLY_DEFAULT_ATTR_NUM (uid,                            "owner ID",                        "%d");
1784
         APPLY_DEFAULT_ATTR_NUM (gid,                            "group ID",                        "%d");
1785
         APPLY_DEFAULT_ATTR_NUM (st_mode,                        "inode type",                      "0%o");
1786
         APPLY_DEFAULT_ATTR_NUM (is_compiled_bootscript,         "compiled script state",           "%d");
1787
         APPLY_DEFAULT_ATTR_NUM (should_follow_symlinks,         "symlink resolution flag",         "%d");
1788
         APPLY_DEFAULT_ATTR_NUM (should_autosymlink_dylib,       "dylib canonical name symlinking", "%d");
1789
         APPLY_DEFAULT_ATTR_NUM (should_keep_ld_output,          "linker output preservation flag", "%d");
1790
         APPLY_DEFAULT_ATTR_NUM (should_ignore_duplicates,       "ignore duplicates flag",          "%d");
1791
         APPLY_DEFAULT_ATTR_NUM (should_allow_nonexistent_files, "ignore nonexistent files flag",   "%d");
17 pmbaty 1792
         #undef APPLY_DEFAULT_ATTR_STR
1793
         #undef APPLY_DEFAULT_ATTR_NUM
1794
         return; // end of line reached, proceed to the next line
1795
      }
1796
      // end of attributes parsing
1797
   } // end of "this line starts with an attributes block"
1798
 
1799
   // there's data in this line. We expect a filename in the IFS. Read it and unescape escaped characters
1800
   string_len = sprintf_s (path_in_ifs, sizeof (path_in_ifs), "%s", (entry_parms.prefix != NULL ? entry_parms.prefix : ""));
1801
   while ((string_len > 0) && (path_in_ifs[string_len - 1] == '/'))
1802
      string_len--; // chop off any trailing slashes from prefix
1803
   write_ptr = &path_in_ifs[string_len];
1804
   *write_ptr++ = '/'; // add ONE trailing slash
1805
   specifiedpathname_start = write_ptr; // remember the specified pathname will start here
1806
   is_quoted_context = (*line_ptr == '"');
1807
   if (is_quoted_context)
1808
      line_ptr++; // skip a possible initial quote
1809
   if (*line_ptr == '/')
1810
   {
1811
      LOG_WARNING ("paths in the IFS file should not begin with a leading '/' in \"%s\" line %d", buildfile_pathname, lineno);
1812
      line_ptr++; // consistency check: paths in the IFS should not begin with a '/'
7 pmbaty 1813
   }
17 pmbaty 1814
   while ((*line_ptr != 0) && ((!is_quoted_context && (*line_ptr != '=') && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"'))))
1815
   {
1816
      if (*line_ptr == '\\')
1817
      {
1818
         line_ptr++;
1819
         *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
1820
      }
1821
      else
1822
         *write_ptr++ = *line_ptr;
1823
      line_ptr++;
1824
   }
1825
   *write_ptr = 0; // terminate the string
1826
   if (is_quoted_context && (*line_ptr == '"'))
1827
      line_ptr++; // skip a possible final quote
7 pmbaty 1828
 
17 pmbaty 1829
   // we reached a space OR an equal sign
1830
   while ((*line_ptr != 0) && isspace (*line_ptr))
1831
      line_ptr++; // skip optional spaces after the filename in the IFS
1832
 
1833
   // do we have an equal sign ?
1834
   if (*line_ptr == '=') // we must be creating either a directory or a file, do we have an equal sign ?
1835
   {
1836
      line_ptr++; // skip the equal sign
1837
      while ((*line_ptr != 0) && isspace (*line_ptr))
1838
         line_ptr++; // skip optional spaces after the equal sign
1839
 
1840
      if (*line_ptr == 0)
1841
      {
1842
         LOG_WARNING ("syntax error in \"%s\" line %d: missing data specification after equal sign (skipping)", buildfile_pathname, lineno);
1843
         return; // invalid symlink specification, skip line
1844
      }
1845
 
1846
      // read the host system's path, it may be either a path or a contents definition. Is it a content definition ?
1847
      if (*line_ptr == '{')
1848
      {
1849
         allocated_size = 0;
1850
 
1851
         line_ptr++; // skip the leading content definition
1852
         is_escaped_char = false;
1853
         for (;;)
1854
         {
1855
            read_char = fgetc (buildfile_fp);
1856
            if (read_char == EOF)
1857
               DIE_WITH_EXITCODE (1, "syntax error in \"%s\" line %d: unterminated contents block (end of file reached)", buildfile_pathname, lineno); // invalid contents block
1858
            else if ((read_char == '\\') && !is_escaped_char)
1859
               is_escaped_char = true; // remember the next char is escaped
1860
            else if ((read_char == '}') && !is_escaped_char)
1861
               break; // found an unescaped closing bracked, stop parsing
1862
            else
1863
            {
1864
               is_escaped_char = false; // any other char, meaning the next one will not be escaped
1865
               if (!should_discard_inline_contents) // only store the contents if we do NOT know the data yet
1866
               {
1867
                  if (entry_parms.data.size == allocated_size) // reallocate in 4 kb blocks
1868
                  {
1869
                     reallocated_ptr = realloc (entry_parms.data.bytes, allocated_size + 4096);
1870
                     ASSERT_WITH_ERRNO (reallocated_ptr);
1871
                     entry_parms.data.bytes = reallocated_ptr;
1872
                     allocated_size += 4096;
1873
                  }
1874
                  entry_parms.data.bytes[entry_parms.data.size++] = read_char;
1875
               }
1876
               if (read_char == '\n')
1877
                  lineno++; // update line counter as we parse the inline content
1878
            }
1879
         } // end for
1880
      }
1881
      else // not a content definition between { brackets }, must be either a pathname on the build host, or the target of a symlink
1882
      {
1883
         is_quoted_context = (*line_ptr == '"');
1884
         if (is_quoted_context)
1885
            line_ptr++; // skip a possible initial quote
1886
         specifiedpathname_start = line_ptr; // remember where the specified pathname starts
1887
         write_ptr = line_ptr; // now unescape all characters
1888
         while ((*line_ptr != 0) && ((!is_quoted_context && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr == '"'))))
1889
         {
1890
            if (*line_ptr == '\\')
1891
            {
1892
               line_ptr++;
1893
               *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
1894
            }
1895
            else
1896
               *write_ptr++ = *line_ptr;
1897
            line_ptr++;
1898
         }
1899
         *write_ptr = 0; // terminate the string
1900
         if (is_quoted_context && (*line_ptr == '"'))
1901
            line_ptr++; // skip a possible final quote
1902
 
1903
         if (S_ISLNK (entry_parms.st_mode)) // are we storing a symlink ?
1904
            ASSERT_WITH_ERRNO (Buffer_InitWithCString (&entry_parms.data, specifiedpathname_start)); // if so, store the symlink target as the dirent's blob data
1905
         else // it's a build host filesystem path
1906
            strcpy_s (path_on_buildhost, sizeof (path_on_buildhost), specifiedpathname_start); // the path on the build host is given after the equal sign
1907
      }
1908
   }
1909
   else // no equal sign, meaning the file will have the same name on the build host filesystem
1910
   {
1911
      // consistency check: symlinks MUST have an equal sign
1912
      if (entry_parms.st_mode == S_IFLNK)
1913
      {
1914
         LOG_WARNING ("syntax error in \"%s\" line %d: missing equal sign and symlink target (skipping)", buildfile_pathname, lineno);
1915
         return; // invalid symlink specification, skip line
1916
      }
1917
 
1918
      strcpy_s (path_on_buildhost, sizeof (path_on_buildhost), specifiedpathname_start); // the path on the build host is the one specified
1919
      sep = strrchr (specifiedpathname_start, '/');
1920
      if (sep != NULL)
1921
         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)
1922
   }
1923
 
1924
   // now add this entry to the image filesystem
1925
   if (S_ISDIR (entry_parms.st_mode))
1926
      entry_parms.st_mode |= entry_parms.dperms;
1927
   else if (S_ISLNK (entry_parms.st_mode))
1928
      entry_parms.st_mode |= 0777; // NOTE: mkifs sets symlink permissions to rwxrwxrwx !?
1929
   else // file or device node
1930
      entry_parms.st_mode |= entry_parms.perms;
1931
 
1932
   add_fsentry (fsentries, fsentry_count, &entry_parms, path_in_ifs, path_on_buildhost); // and add filesystem entry
1933
 
1934
   if (entry_parms.data.bytes != NULL)
1935
      free (entry_parms.data.bytes); // if blob data was allocated, free it
1936
 
1937
   return; //  finished parsing that line
1 pmbaty 1938
}
1939
 
1940
 
1941
int main (int argc, char **argv)
1942
{
1943
   // program entrypoint
1944
 
16 pmbaty 1945
   typedef struct ifs_offsets_s
1946
   {
1947
      size_t startupheader;
1948
      size_t startuptrailer;
1949
      size_t imageheader;
1950
      size_t imagedir;
1951
      size_t imagetrailer;
1952
   } ifs_offsets_t;
1953
   typedef struct ifs_s
1954
   {
1955
      buffer_t data;
1956
      ifs_offsets_t offsets;
1957
      size_t final_size; // final size: not known (because not set) until everything has been written
1958
   } ifs_t;
1 pmbaty 1959
 
19 pmbaty 1960
   startup_header_t startup_header = { 0 }; // output IFS's startup header
1961
   startup_trailer_v2_t startup_trailer = { 0 }; // output IFS's startup trailer (version 2, with SHA-512 checksum and int32 checksum)
1962
   image_header_t image_header = { 0 }; // output IFS's imagefs header
1963
   image_trailer_v2_t image_trailer = { 0 }; // output IFS's imagefs trailer (version 2, with SHA-512 checksum and int32 checksum)
1964
   fsentry_t *fsentries = NULL; // output IFS's filesystem entries
1965
   size_t fsentry_count = 0; // number of entries in the IFS filesystem
1966
   parms_t default_parms = { // default parameters for a filesystem entry
7 pmbaty 1967
      .dperms = 0755,
1968
      .perms = 0644,
1969
      .mtime = UINT32_MAX,
1970
      .mtime_for_inline_files = UINT32_MAX,
1971
      .prefix = "/proc/boot",
1972
      .should_follow_symlinks = true, // [+|-followlink]
1973
      .should_autosymlink_dylib = true, // [+|-autolink]
1974
   };
19 pmbaty 1975
   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 1976
 
7 pmbaty 1977
   char path_on_buildhost[MAXPATHLEN] = "";
1978
   char path_in_ifs[MAXPATHLEN] = "";
19 pmbaty 1979
   const char *ifs_pathname = NULL;
1980
   const char *rootdir_pathname = NULL;
1981
   const fsentry_t *fsentry;
1 pmbaty 1982
   void *reallocated_ptr;
17 pmbaty 1983
   size_t reallocated_size;
7 pmbaty 1984
   size_t available_space;
1 pmbaty 1985
   size_t fsentry_index;
7 pmbaty 1986
   size_t largest_index;
1987
   size_t largest_size;
17 pmbaty 1988
   size_t imgdir_size;
1 pmbaty 1989
   size_t curr_offset;
16 pmbaty 1990
   ifs_t ifs = { 0 };
8 pmbaty 1991
   int32_t checksum;
16 pmbaty 1992
   char *first_pathname = NULL;
1993
   char *second_pathname = NULL;
19 pmbaty 1994
   char *third_pathname = NULL;
1 pmbaty 1995
   char *sep;
1996
   int arg_index;
1997
   bool is_quoted_context = false;
1998
   bool is_escaped_char = false;
11 pmbaty 1999
   bool should_discard_inline_contents = false;
1 pmbaty 2000
   bool want_info = false;
10 pmbaty 2001
   bool want_everything = false;
1 pmbaty 2002
   bool want_help = false;
14 pmbaty 2003
   bool want_dump = false;
16 pmbaty 2004
   bool want_strip = false;
15 pmbaty 2005
   bool want_hexdump = false;
5 pmbaty 2006
   bool is_foreign_endianness;
1 pmbaty 2007
   FILE *buildfile_fp;
2008
 
17 pmbaty 2009
   // initialize stuff
2010
   saved_ELF_sections = (char **) malloc (4 * sizeof (char *));
2011
   ASSERT_WITH_ERRNO (saved_ELF_sections);
2012
   saved_ELF_sections[0] = "QNX_info"; // NOTE: MUST BE THE FIRST ONE as we artificially shrink down the array to 1 when using it for boot ELF files
2013
   saved_ELF_sections[1] = ".gnu_debuglink";
2014
   saved_ELF_sections[2] = "QNX_usage";
2015
   saved_ELF_sections[3] = ".note.gnu.build-id"; // undocumented by QNX, but nonetheless preserved
2016
   saved_ELF_section_count = 4;
2017
 
1 pmbaty 2018
   // parse arguments
2019
   for (arg_index = 1; arg_index < argc; arg_index++)
2020
   {
2021
      if ((strcmp (argv[arg_index], "--bootfile") == 0) && (arg_index + 1 < argc)) // --bootfile path/to/blob.bin
2022
         bootfile_pathname = argv[++arg_index];
2023
      else if ((strcmp (argv[arg_index], "--startupfile") == 0) && (arg_index + 1 < argc)) // --startupfile path/to/blob.bin@0x1030
2024
      {
2025
         sep = strchr (argv[++arg_index], '@');
2026
         if ((sep == NULL) || (sep[1] == 0))
15 pmbaty 2027
            DIE_WITH_EXITCODE (1, "the --startupfile arguments expects <pathname>@<entrypoint_from_image_base>");
1 pmbaty 2028
         *sep = 0;
2029
         startupfile_pathname = argv[arg_index];
2030
         startupfile_ep_from_imagebase = (size_t) read_integer (sep + 1);
2031
      }
20 pmbaty 2032
      else if ((strcmp (argv[arg_index], "--kerneloffs") == 0) && (arg_index + 1 < argc)) // --kerneloffs 0x32000 (undocumented)
2033
         kernelfile_offset = (size_t) read_integer (argv[++arg_index]);
19 pmbaty 2034
      else if ((strcmp (argv[arg_index], "-a") == 0) && (arg_index + 1 < argc)) // -a suffix
2035
         sym_suffix = argv[++arg_index];
7 pmbaty 2036
      else if (strcmp (argv[arg_index], "-n") == 0)
2037
         default_parms.mtime_for_inline_files = 0; // inline files should have a mtime set to zero
2038
      else if (strcmp (argv[arg_index], "-nn") == 0)
2039
      {
11 pmbaty 2040
         default_parms.mtime = 0; // *all* files should have a mtime set to zero
7 pmbaty 2041
         default_parms.mtime_for_inline_files = 0;
2042
      }
14 pmbaty 2043
      else if ((strcmp (argv[arg_index], "--outdir") == 0) && (arg_index + 1 < argc)) // --outdir path
16 pmbaty 2044
         second_pathname = argv[++arg_index];
2045
      else if ((strcmp (argv[arg_index], "--outfile") == 0) && (arg_index + 1 < argc)) // --outfile pathname
2046
         second_pathname = argv[++arg_index];
1 pmbaty 2047
      else if (strcmp (argv[arg_index], "--info") == 0)
2048
         want_info = true;
14 pmbaty 2049
      else if (strcmp (argv[arg_index], "--dump") == 0)
2050
         want_dump = true;
15 pmbaty 2051
      else if (strcmp (argv[arg_index], "--hexdump") == 0) // voluntarily undocumented
2052
         want_hexdump = true;
16 pmbaty 2053
      else if (strcmp (argv[arg_index], "--strip") == 0)
2054
         want_strip = true;
10 pmbaty 2055
      else if (strcmp (argv[arg_index], "--everything") == 0)
2056
         want_everything = true;
15 pmbaty 2057
      else if (strncmp (argv[arg_index], "-v", 2) == 0) // -v[....]
2058
         verbose_level += (int) strlen (argv[arg_index] + 1); // increase verbosity by the number of characters in this flag
17 pmbaty 2059
      else if ((strcmp (argv[arg_index], "-l") == 0) && (arg_index + 1 < argc))
2060
         arg_index++; // these args will be parsed once the build file is open
2061
      else if ((strcmp (argv[arg_index], "-r") == 0) && (arg_index + 1 < argc))
2062
      {
2063
         reallocated_size = (SEARCH_PATH != NULL ? strlen (SEARCH_PATH) + 1 : 0) + strlen (argv[arg_index + 1]) + 1;
2064
         reallocated_ptr = realloc (SEARCH_PATH, reallocated_size); // grow search prefixes array
2065
         ASSERT_WITH_ERRNO (reallocated_ptr);
2066
         if (SEARCH_PATH != NULL)
2067
            strcat_s (reallocated_ptr, reallocated_size, PATH_SEP);
2068
         strcat_s (reallocated_ptr, reallocated_size, argv[++arg_index]); // stack up another search prefix
2069
         SEARCH_PATH = reallocated_ptr;
2070
      }
2071
      else if ((strcmp (argv[arg_index], "-s") == 0) && (arg_index + 1 < argc))
2072
      {
2073
         reallocated_ptr = realloc (saved_ELF_sections, (saved_ELF_section_count + 1) * sizeof (char *)); // grow ELF sections array
2074
         ASSERT_WITH_ERRNO (reallocated_ptr);
2075
         saved_ELF_sections = reallocated_ptr;
2076
         saved_ELF_sections[saved_ELF_section_count++] = argv[++arg_index]; // stack up another ELF section name to preserve
2077
      }
1 pmbaty 2078
      else if ((strcmp (argv[arg_index], "-?") == 0) || (strcmp (argv[arg_index], "--help") == 0))
2079
         want_help = true;
20 pmbaty 2080
      else if ((first_pathname == NULL) && (*argv[arg_index] != '-'))
16 pmbaty 2081
         first_pathname = argv[arg_index];
20 pmbaty 2082
      else if ((second_pathname == NULL) && (*argv[arg_index] != '-'))
16 pmbaty 2083
         second_pathname = argv[arg_index];
20 pmbaty 2084
      else if ((third_pathname == NULL) && (*argv[arg_index] != '-'))
19 pmbaty 2085
         third_pathname = argv[arg_index];
16 pmbaty 2086
      else
2087
         DIE_WITH_EXITCODE (1, "unrecognized option: '%s'", argv[arg_index]);
1 pmbaty 2088
   }
2089
 
19 pmbaty 2090
   // do we want to display help ? (TODO: everything that's commented out is pending implementation)
2091
   if (want_help)
1 pmbaty 2092
   {
16 pmbaty 2093
      FILE *out = (want_help ? stdout : stderr); // select the right output channel
2094
      fprintf (out, "ifstool - QNX in-kernel filesystem creation utility by Pierre-Marie Baty <pm@pmbaty.com>\n");
2095
      fprintf (out, "          version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
4 pmbaty 2096
      if (!want_help)
16 pmbaty 2097
         fprintf (out, "error: missing parameters\n");
2098
      fprintf (out, "usage:\n");
2099
      fprintf (out, "    ifstool --info [--everything] <ifs file>\n");
2100
      fprintf (out, "    ifstool --dump [--outdir <path>] <ifs file>\n");
2101
      fprintf (out, "    ifstool --strip [--outfile <pathname>] <ELF file>\n");
17 pmbaty 2102
      fprintf (out, "    ifstool [-?|--help]\n");
19 pmbaty 2103
      // mkifs [-?] [-l inputline] [-n[n]] [-o directory] [-p patchfile] [-r rootdir] [-s section] [-v] [buildfile] [directory] [outputfile]
21 pmbaty 2104
      fprintf (out, "    ifstool [--bootfile <pathname>] [--startupfile <pathname>@<EP_from_imgbase>] [--kerneloffs <fileoffs>] [-a suffix] [-l inputline] [-n[n]] [-r rootdir] [-s section] [-v[...]] [buildfile] [directory] [outputfile]\n");
2105
      fprintf (out, "NOTE: the compiler mode requires predigested boot and startup files produced by mkifs.\n");
17 pmbaty 2106
      fprintf (out, "options:\n");
2107
      fprintf (out, "    -?       Display some help information.\n");
19 pmbaty 2108
      fprintf (out, "    -a .ext  Append a suffix to symbol files generated via [+keeplinked].\n");
17 pmbaty 2109
      fprintf (out, "    -l line  Process line before interpreting the buildfile. Input lines given\n");
2110
      fprintf (out, "             to mkifs should be quoted to prevent interpretation by the shell\n");
2111
      fprintf (out, "             (especially as mkifs input lines often contain spaces). Multiple\n");
2112
      fprintf (out, "             -l options are processed in the order specified. No default.\n");
2113
      fprintf (out, "    -n[n]    Force the modification times of all inline files to be 0. If you\n");
2114
      fprintf (out, "             specify -nn, mkifs sets the modification times of all files to 0.\n");
2115
      fprintf (out, "             When mkifs adds files to an IFS image, it uses the timestamp info\n");
2116
      fprintf (out, "             from the file on the host machine. If mkifs is creating an inline\n");
2117
      fprintf (out, "             file (which doesn't exist on the host machine), it must generate\n");
2118
      fprintf (out, "             its own timestamp information. By default, it's the time at which\n");
2119
      fprintf (out, "             the image is generated. This results in different checksum values\n");
2120
      fprintf (out, "             for two identical builds, because the file's times are different.\n");
2121
      fprintf (out, "             If you use -n, the checksum value is the same on all identical\n");
2122
      fprintf (out, "             builds. The -nn option addresses a quirk in NTFS with daylight\n");
2123
      fprintf (out, "             savings time. This forces the modification time for all files in\n");
2124
      fprintf (out, "             the IFS image to be set to 0. This ensures that subsequent builds\n");
2125
      fprintf (out, "             of the same IFS image have the same checksum.");
2126
//      fprintf (out, "    -o dir   Specify a directory to be used for all permanent build artifacts,\n");
2127
//      fprintf (out, "             other than the output image itself. The most common example is\n");
2128
//      fprintf (out, "             the .sym files generated by the [+keeplinked] attribute.\n");
2129
//      fprintf (out, "    -p file  Apply patching instructions from this file.\n");
2130
      fprintf (out, "    -r dir   When searching for host files to be included in the image, search\n");
2131
      fprintf (out, "             the default paths used for storing binaries within the specified\n");
2132
      fprintf (out, "             directory before searching the default paths within $QNX_TARGET.\n");
2133
      fprintf (out, "             You can define multiple -r options; each adds a set of paths to\n");
2134
      fprintf (out, "             search for files. The -r options are evaluated from left to right\n");
2135
      fprintf (out, "             meaning the paths prefixed with the first (leftmost) rootdir are\n");
2136
      fprintf (out, "             searched first, then those prefixed with the second rootdir, and\n");
2137
      fprintf (out, "             so on.\n");
2138
      fprintf (out, "             Normally, mkifs searches any paths defined in $MKIFS_PATH when\n");
2139
      fprintf (out, "             it was called and then the default paths within $QNX_TARGET. The\n");
2140
      fprintf (out, "             default paths are based on the CPU architecture specified by\n");
2141
      fprintf (out, "             $PROCESSOR and $PROCESSOR_BASE. If you specify -r options, mkifs\n");
2142
      fprintf (out, "             searches the default paths prefixed with each dir variable before\n");
2143
      fprintf (out, "             searching those within $QNX_TARGET. These paths are:\n");
2144
      fprintf (out, "               dir/${PROCESSOR}/sbin\n");
2145
      fprintf (out, "               dir/${PROCESSOR}/usr/sbin\n");
2146
      fprintf (out, "               dir/${PROCESSOR}/boot/sys\n");
2147
      fprintf (out, "               dir/${PROCESSOR_BASE}/boot/sys\n");
2148
      fprintf (out, "               dir/${PROCESSOR}/bin\n");
2149
      fprintf (out, "               dir/${PROCESSOR}/usr/bin\n");
2150
      fprintf (out, "               dir/${PROCESSOR}/lib\n");
2151
      fprintf (out, "               dir/${PROCESSOR}/lib/dll\n");
2152
      fprintf (out, "               dir/${PROCESSOR}/usr/lib\n");
2153
      fprintf (out, "             NOTE: The structure of the directory paths under dir must be\n");
2154
      fprintf (out, "             identical to that of the default paths under $QNX_TARGET, but the\n");
2155
      fprintf (out, "             root dir itself may be any path you choose. For example, if you\n");
2156
      fprintf (out, "             wanted to include /scratch/aarch64le/sbin/devb-sata, you would\n");
2157
      fprintf (out, "             specify a -r option like this:\n");
2158
      fprintf (out, "               -r /scratch\n");
2159
      fprintf (out, "             Note that you don't include $PROCESSOR or $PROCESSOR_BASE in dir.\n");
2160
      fprintf (out, "    -s name  Don't strip the named section from ELF executables when creating\n");
2161
      fprintf (out, "             an IFS image. You can use this option more than once to specify\n");
2162
      fprintf (out, "             additional sections. By default, mkifs doesn't strip:\n");
2163
      fprintf (out, "               .gnu_debuglink - the name and checksum of the debug info file\n");
2164
      fprintf (out, "               QNX_info       - build properties\n");
2165
      fprintf (out, "               QNX_usage      - usage message\n");
2166
      fprintf (out, "             You can use the keepsection attribute to specify the sections\n");
2167
      fprintf (out, "             that are not to be stripped from specific files in the image. For\n");
2168
      fprintf (out, "             files in the bootstrap section (like startup or procnto), the\n");
2169
      fprintf (out, "             global keepsection list affected by -s does not apply to these\n");
2170
      fprintf (out, "             files. For them, only the QNX_info section is kept.\n");
2171
      fprintf (out, "    -v[v..]  Operate verbosely. Specifying additional v options increases the\n");
2172
      fprintf (out, "             verbosity.\n");
1 pmbaty 2173
      exit (want_help ? 0 : 1);
2174
   }
2175
 
19 pmbaty 2176
   // else do we want info about a particular IFS ? if so, dissecate it
2177
   else if (want_info)
16 pmbaty 2178
      exit (dump_ifs_info (first_pathname, want_everything));
2 pmbaty 2179
 
14 pmbaty 2180
   // else do we want to dump its contents ? if so, do so
2181
   else if (want_dump)
16 pmbaty 2182
      exit (dump_ifs_contents (first_pathname, (second_pathname != NULL ? second_pathname : ".")));
14 pmbaty 2183
 
15 pmbaty 2184
   // else do we want to hex dump a file ? (this is voluntarily undocumented)
2185
   else if (want_hexdump)
16 pmbaty 2186
      exit (dump_file_hex (first_pathname));
2187
 
2188
   // else do we want to strip an ELF file ? if so, do so
2189
   else if (want_strip)
15 pmbaty 2190
   {
16 pmbaty 2191
      buffer_t file;
2192
      ASSERT (Buffer_ReadFromFile (&file, first_pathname), "can't open \"%s\" for reading: %s", first_pathname, strerror (errno));
20 pmbaty 2193
      ASSERT (Buffer_StripELFFile (&file, (const char **) saved_ELF_sections, saved_ELF_section_count, false, first_pathname), "error stripping \"%s\": %s", first_pathname, strerror (errno));
16 pmbaty 2194
      ASSERT_WITH_ERRNO (Buffer_WriteToFile (&file, (second_pathname != NULL ? second_pathname : "<stdout>")));
15 pmbaty 2195
      exit (0);
2196
   }
2197
 
16 pmbaty 2198
   // we want to CREATE an IFS file
2199
   buildfile_pathname = first_pathname; // assign the pathnames properly
19 pmbaty 2200
   ifs_pathname = (third_pathname != NULL ? third_pathname : second_pathname); // this is some curious handling of cmdline args, but that's the way mkxfs does it
2201
   rootdir_pathname = (third_pathname != NULL ? second_pathname : NULL);
16 pmbaty 2202
 
1 pmbaty 2203
   // make sure we have ${QNX_TARGET} pointing somewhere
2204
   QNX_TARGET = getenv ("QNX_TARGET");
2205
   if (QNX_TARGET == NULL)
15 pmbaty 2206
      DIE_WITH_EXITCODE (1, "the QNX_TARGET environment variable is not set");
1 pmbaty 2207
   else if (access (QNX_TARGET, 0) != 0)
15 pmbaty 2208
      DIE_WITH_EXITCODE (1, "the QNX_TARGET environment variable doesn't point to an existing directory");
1 pmbaty 2209
 
2210
   // open build file
19 pmbaty 2211
   if ((buildfile_pathname != NULL) && (strcmp (buildfile_pathname, "-") != 0))
2212
   {
2213
      fopen_s (&buildfile_fp, buildfile_pathname, "rb"); // open it
2214
      if (buildfile_fp == NULL)
2215
         DIE_WITH_EXITCODE (1, "unable to open build file \"%s\" for reading: %s", buildfile_pathname, strerror (errno));
2216
   }
2217
   else // no build file specified: use stdin
2218
   {
2219
      buildfile_pathname = "<stdin>";
2220
      buildfile_fp = stdin;
2221
   }
1 pmbaty 2222
 
2223
   // stack up filesystem entries
2224
   memcpy (&entry_parms, &default_parms, sizeof (default_parms));
2225
   entry_parms.st_mode = S_IFDIR | default_parms.dperms;
7 pmbaty 2226
   add_fsentry (&fsentries, &fsentry_count, &entry_parms, "", NULL); // add the root dir first
1 pmbaty 2227
 
17 pmbaty 2228
   // parse -l arguments before everything else
2229
   for (arg_index = 1; arg_index < argc; arg_index++)
2230
      if ((strcmp (argv[arg_index], "-l") == 0) && (arg_index + 1 < argc))
2231
         parse_line (NULL, argv[++arg_index], &fsentries, &fsentry_count, &default_parms);
2232
 
11 pmbaty 2233
   // parse the IFS build file line per line
1 pmbaty 2234
   while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL)
2235
   {
7 pmbaty 2236
      if (current_line != NULL)
2237
         free (current_line);
2238
      current_line = strdup (line_buffer);
16 pmbaty 2239
      ASSERT_WITH_ERRNO (current_line);
1 pmbaty 2240
      lineno++; // keep track of current line number
17 pmbaty 2241
      parse_line (buildfile_fp, line_buffer, &fsentries, &fsentry_count, &default_parms);
2242
   }
1 pmbaty 2243
 
17 pmbaty 2244
   fclose (buildfile_fp); // finished parsing the build file
1 pmbaty 2245
 
19 pmbaty 2246
   // if a root dir was specified, open it as a directory and recursively add all of its contents to the filesystem
2247
   if (rootdir_pathname != NULL)
2248
      add_directory_contents_recursively (&fsentries, &fsentry_count, rootdir_pathname, strlen (rootdir_pathname), &default_parms);
1 pmbaty 2249
 
16 pmbaty 2250
   //////////////////////////////////
2251
   // start constructing the IFS file
2252
 
2253
   Buffer_Initialize (&ifs.data);
2254
 
2 pmbaty 2255
   // do we have a startup file ? if so, this is a bootable image
2256
   if (startupfile_pathname != NULL)
1 pmbaty 2257
   {
2258
      // write boot prefix
11 pmbaty 2259
      // ######################################################################################################################################################################################################################################
2260
      // # FIXME: figure out how to re-create it
2261
      // ######################################################################################################################################################################################################################################
16 pmbaty 2262
      buffer_t file;
2263
      if (!Buffer_ReadFromFile (&file, bootfile_pathname))
2264
         DIE_WITH_EXITCODE (1, "failed to open \"%s\" for reading: %s", bootfile_pathname, strerror (errno));
2265
      ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&ifs.data, &file)); // write boot blob
2266
      Buffer_Forget (&file);
2267
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2268
 
16 pmbaty 2269
      ifs.offsets.startupheader = ifs.data.size; // save startup header offset for future use
1 pmbaty 2270
      memset (&startup_header, 0, sizeof (startup_header)); // prepare startup header
2271
      memcpy (startup_header.signature, "\xeb\x7e\xff\x00", 4); // startup header signature, i.e. 0xff7eeb
2272
      startup_header.version       = 1;
2273
      startup_header.flags1        = STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2; // flags, 0x21 (STARTUP_HDR_FLAGS1_VIRTUAL | STARTUP_HDR_FLAGS1_TRAILER_V2)
2274
      startup_header.header_size   = sizeof (startup_header); // 256
2275
      if (strcmp (image_processor, "x86_64") == 0)
10 pmbaty 2276
         startup_header.machine = ELF_MACHINE_X86_64; // EM_X86_64
1 pmbaty 2277
      else if (strcmp (image_processor, "aarch64le") == 0)
10 pmbaty 2278
         startup_header.machine = ELF_MACHINE_AARCH64; // EM_AARCH64
1 pmbaty 2279
      else
15 pmbaty 2280
         DIE_WITH_EXITCODE (1, "unsupported processor type '%s' found in build file \"%s\"", image_processor, buildfile_pathname); // should not happen
1 pmbaty 2281
      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 2282
      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 2283
      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)
2284
      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)
2285
      startup_header.startup_size  = WILL_BE_FILLED_LATER;                                  // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
2 pmbaty 2286
      startup_header.stored_size   = WILL_BE_FILLED_LATER;                                  // [I ] Size of entire image, here 0x00cd6128 (same as ram_size)
1 pmbaty 2287
      startup_header.imagefs_size  = WILL_BE_FILLED_LATER;                                  // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
2 pmbaty 2288
      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 2289
      ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &startup_header, sizeof (startup_header))); // write startup header
2290
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2291
 
2292
      // ######################################################################################################################################################################################################################################
11 pmbaty 2293
      // # FIXME: figure out how to re-create it:
2294
      // first: open "startup-x86" ELF file,
2295
      //        lookup section headers table (there is no program headers table in this one)
2296
      //        FIXME: figure out something in there where the result is 0x1401030 !!!
2297
      // 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
2298
      // then: parse resulting ELF file, take all program segments and concatenate them --> this is the blob (FIXME: wrong?)
1 pmbaty 2299
      // ######################################################################################################################################################################################################################################
11 pmbaty 2300
#if 0 // nonworking
16 pmbaty 2301
      // <deleted>
11 pmbaty 2302
#else // working
16 pmbaty 2303
      if (!Buffer_ReadFromFile (&file, startupfile_pathname))
2304
         DIE_WITH_EXITCODE (1, "failed to open \"%s\" for reading: %s", startupfile_pathname, strerror (errno));
2305
      ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&ifs.data, &file)); // write startup blob
2306
      Buffer_Forget (&file);
11 pmbaty 2307
#endif // working
16 pmbaty 2308
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2309
 
16 pmbaty 2310
      ifs.offsets.startuptrailer = ifs.data.size; // save startup trailer offset for future use
2311
      ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &startup_trailer, sizeof (startup_trailer))); // write startup trailer
2312
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2313
   }
2314
 
16 pmbaty 2315
   ifs.offsets.imageheader = ifs.data.size; // save image header offset for future use
1 pmbaty 2316
   memset (&image_header, 0, sizeof (image_header)); // prepare image header
2317
   memcpy (&image_header.signature, "imagefs", 7); // image filesystem signature, i.e. "imagefs"
2318
   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)
2319
   image_header.image_size    = WILL_BE_FILLED_LATER; // size from header to end of trailer (here 0xca6fe0 or 13 266 912)
7 pmbaty 2320
   image_header.hdr_dir_size  = WILL_BE_FILLED_LATER; // size from header to last dirent (here 0x12b8 or 4792)
1 pmbaty 2321
   image_header.dir_offset    = sizeof (image_header); // offset from header to first dirent (here 0x5c or 92)
2322
   image_header.boot_ino[0]   = image_kernel_ino; // inode of files for bootstrap p[ro?]g[ra?]ms (here 0xa0000002, 0, 0, 0)
2323
   image_header.script_ino    = image_bootscript_ino; // inode of file for script (here 3)
2324
   image_header.mountpoint[0] = '/'; // default mountpoint for image ("/" + "\0\0\0")
16 pmbaty 2325
   ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &image_header, sizeof (image_header))); // write image header
2326
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2327
 
6 pmbaty 2328
   // write image directory (with the wrong file offsets)
16 pmbaty 2329
   ifs.offsets.imagedir = ifs.data.size; // save image directory offset for future use
2330
   curr_offset = ifs.offsets.imagedir;
1 pmbaty 2331
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
16 pmbaty 2332
   {
2333
      Buffer_WriteIFSDirectoryEntryAt (&ifs.data, curr_offset, &fsentries[fsentry_index]); // write each dirent (the unknown fields will be fixed later)
2334
      curr_offset += fsentries[fsentry_index].header.size; // advance to the next one
2335
   }
2336
   ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&ifs.data, "\0\0\0\0")); // there seems to be 4 bytes of padding after the image directory
2337
   imgdir_size = ifs.data.size - ifs.offsets.imagedir; // measure image dir size and save it for future use
1 pmbaty 2338
 
20 pmbaty 2339
   // is it a bootable image with a startup file ?
2340
   if (startupfile_pathname != NULL)
1 pmbaty 2341
   {
7 pmbaty 2342
      // start by writing the startup script data blob, if we have one
2343
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2344
         if (fsentries[fsentry_index].header.ino == image_bootscript_ino)
8 pmbaty 2345
            break; // locate the startup script directory entry
7 pmbaty 2346
      if (fsentry_index < fsentry_count) // found it ?
1 pmbaty 2347
      {
16 pmbaty 2348
         if (ifs.data.size + fsentries[fsentry_index].u.file.size >= kernelfile_offset)
20 pmbaty 2349
            DIE_WITH_EXITCODE (1, "the compiled startup script is too big (%zd bytes, max is %zd) to fit at current offset %zd. Use --kerneloffs <addr> to relocate (warning: untested)", (size_t) fsentries[fsentry_index].u.file.size, kernelfile_offset - ifs.data.size, ifs.data.size);
16 pmbaty 2350
         fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2351
         Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
1 pmbaty 2352
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2353
      }
7 pmbaty 2354
 
2355
      // now write the filesystem entries that may fit before the kernel
2356
      for (;;)
2357
      {
16 pmbaty 2358
         available_space = kernelfile_offset - ifs.data.size; // measure the available space until the kernel
7 pmbaty 2359
 
2360
         // look for the biggest one that can fit
2361
         largest_index = 0;
8 pmbaty 2362
         largest_size = 0;
7 pmbaty 2363
         for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2364
         {
2365
            if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written || (fsentries[fsentry_index].u.file.size > available_space))
2366
               continue; // skip all entries that don't have a separate data block, those who were written already and those that wouldn't fit
2367
            if (fsentries[fsentry_index].u.file.size > largest_size)
2368
            {
2369
               largest_size = fsentries[fsentry_index].u.file.size;
2370
               largest_index = fsentry_index;
2371
            }
2372
         }
8 pmbaty 2373
         if (largest_size == 0)
7 pmbaty 2374
            break; // found none ? if so, stop searching
16 pmbaty 2375
         fsentry_index = largest_index;
7 pmbaty 2376
 
16 pmbaty 2377
         fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2378
         Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
2379
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
7 pmbaty 2380
      }
16 pmbaty 2381
      LOG_INFO ("Last written offset: 0x%zx", ifs.data.size);
15 pmbaty 2382
      LOG_INFO ("Kernel file offset: 0x%zx", kernelfile_offset);
16 pmbaty 2383
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, kernelfile_offset)); // reach the kernel offset
1 pmbaty 2384
 
8 pmbaty 2385
      // now write the QNX kernel
2386
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2387
         if (fsentries[fsentry_index].header.ino == image_kernel_ino)
2388
            break; // locate the kernel directory entry (can't fail)
16 pmbaty 2389
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2390
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write kernel file data
8 pmbaty 2391
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1 pmbaty 2392
   }
2393
 
8 pmbaty 2394
   // then write all the other files by increasing inode number: ELF files first
7 pmbaty 2395
   for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1 pmbaty 2396
   {
8 pmbaty 2397
      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
2398
          || (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 2399
         continue; // skip all entries that don't have a separate data block and those who were written already
16 pmbaty 2400
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2401
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
1 pmbaty 2402
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2403
   }
8 pmbaty 2404
   for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) // other files (non-ELF, e.g. scripts and data files) last
2405
   {
2406
      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
2407
         continue; // skip all entries that don't have a separate data block and those who were written already
16 pmbaty 2408
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2409
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
8 pmbaty 2410
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2411
   }
16 pmbaty 2412
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2413
 
2414
   // finally, write trailer (including empty checksum)
16 pmbaty 2415
   ifs.offsets.imagetrailer = ifs.data.size; // save image trailer offset for future use
2416
   ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &image_trailer, sizeof (image_trailer))); // write image trailer
2417
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2418
 
2419
   // if we need to pad it to a specific length, do so
16 pmbaty 2420
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, image_totalsize));
2421
   ifs.final_size = ifs.data.size; // and this is the final size of the IFS
1 pmbaty 2422
 
2423
   // see if we are past the image max size, in which case it's an error
16 pmbaty 2424
   if (ifs.final_size > image_maxsize)
19 pmbaty 2425
      DIE_WITH_EXITCODE (1, "image file size %zd exceeds max size (%zd)", ifs.final_size, (size_t) image_maxsize);
1 pmbaty 2426
 
2 pmbaty 2427
   // do we have a startup file ? if so, this is a bootable image
2428
   if (startupfile_pathname != NULL)
1 pmbaty 2429
   {
16 pmbaty 2430
      // patch the startup header with its final values
2431
      startup_header.startup_size = (uint32_t) (ifs.offsets.imageheader - ifs.offsets.startupheader); // size of startup header up to image header
2432
      startup_header.imagefs_size = (uint32_t) (ifs.final_size - ifs.offsets.imageheader); // size of uncompressed imagefs
2433
      startup_header.ram_size     = (uint32_t) (ifs.final_size - ifs.offsets.startupheader);
2434
      startup_header.stored_size  = (uint32_t) (ifs.final_size - ifs.offsets.startupheader);
2435
      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 2436
   }
1 pmbaty 2437
 
8 pmbaty 2438
   // rewrite image header with final values
16 pmbaty 2439
   image_header.image_size = (uint32_t) (ifs.final_size - ifs.offsets.imageheader); // size of uncompressed imagefs
8 pmbaty 2440
   image_header.hdr_dir_size = sizeof (image_header) + (uint32_t) imgdir_size; // size from start of image header to last dirent
16 pmbaty 2441
   ASSERT_WITH_ERRNO (Buffer_WriteAt (&ifs.data, ifs.offsets.imageheader, &image_header, sizeof (image_header))); // write image header
8 pmbaty 2442
 
2443
   // rewrite image directory with final offset values
2444
   if (image_header.flags & IMAGE_FLAGS_SORTED)
16 pmbaty 2445
      qsort (&fsentries[1], fsentry_count - 1, sizeof (fsentry_t), fsentry_compare_pathnames_cb); // sort the filesystem entries by pathname if necessary
2446
   curr_offset = ifs.offsets.imagedir; // position ourselves at the beginning of the image directory
8 pmbaty 2447
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
16 pmbaty 2448
   {
2449
      Buffer_WriteIFSDirectoryEntryAt (&ifs.data, curr_offset, &fsentries[fsentry_index]); // rewrite each dirent
2450
      curr_offset += fsentries[fsentry_index].header.size; // advance to the next one
2451
   }
8 pmbaty 2452
 
2453
   // ALL CHECKSUMS AT THE VERY END
2454
 
2455
   // do we have a startup file ? if so, this is a bootable image
2456
   if (startupfile_pathname != NULL)
2457
   {
1 pmbaty 2458
      // compute SHA-512 checksum and V1 checksum of startup block
6 pmbaty 2459
      if (   ( (startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
2460
          || (!(startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
2461
         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
2462
      else
2463
         is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 2464
 
16 pmbaty 2465
      if (startup_header.flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2) // is it a V2 trailer ?
2466
      {
2467
         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
2468
         checksum = update_checksum (&ifs.data.bytes[ifs.offsets.startupheader], ifs.offsets.startuptrailer + SHA512_DIGEST_LENGTH - ifs.offsets.startupheader, is_foreign_endianness); // compute old checksum
2469
         memcpy (&ifs.data.bytes[ifs.offsets.startuptrailer + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
2470
      }
2471
      else // old V1 trailer
2472
      {
2473
         checksum = update_checksum (&ifs.data.bytes[ifs.offsets.startupheader], ifs.offsets.startuptrailer - ifs.offsets.startupheader, is_foreign_endianness); // compute old checksum
2474
         memcpy (&ifs.data.bytes[ifs.offsets.startuptrailer], &checksum, 4); // and write it in place
2475
      }
1 pmbaty 2476
   }
2477
 
2478
   // compute SHA-512 checksum and V1 checksum of image block
5 pmbaty 2479
   if (   ( (image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
2480
       || (!(image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
2481
      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
2482
   else
2483
      is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 2484
 
16 pmbaty 2485
   if (image_header.flags & IMAGE_FLAGS_TRAILER_V2) // is it a V2 trailer ?
2 pmbaty 2486
   {
16 pmbaty 2487
      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
2488
      checksum = update_checksum (&ifs.data.bytes[ifs.offsets.imageheader], ifs.offsets.imagetrailer + SHA512_DIGEST_LENGTH - ifs.offsets.imageheader, is_foreign_endianness); // compute old checksum
2489
      memcpy (&ifs.data.bytes[ifs.offsets.imagetrailer + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
2 pmbaty 2490
   }
16 pmbaty 2491
   else // old V1 trailer
3 pmbaty 2492
   {
16 pmbaty 2493
      checksum = update_checksum (&ifs.data.bytes[ifs.offsets.imageheader], ifs.offsets.imagetrailer - ifs.offsets.imageheader, is_foreign_endianness); // compute old checksum
2494
      memcpy (&ifs.data.bytes[ifs.offsets.imagetrailer], &checksum, 4); // and write it in place
3 pmbaty 2495
   }
2 pmbaty 2496
 
16 pmbaty 2497
   // now rewrite IFS with the correct checksums
19 pmbaty 2498
   ASSERT_WITH_ERRNO (Buffer_WriteToFile (&ifs.data, (ifs_pathname != NULL ? ifs_pathname : "<stdout>")));
14 pmbaty 2499
 
16 pmbaty 2500
   // finished, cleanup
2501
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
14 pmbaty 2502
   {
19 pmbaty 2503
      fsentry = &fsentries[fsentry_index]; // quick access to filesystem entry
2504
      if (S_ISDIR (fsentry->header.mode))
2505
         free (fsentry->u.dir.path);
2506
      else if (S_ISLNK (fsentry->header.mode))
2507
      {
2508
         free (fsentry->u.symlink.path);
2509
         free (fsentry->u.symlink.contents);
2510
      }
2511
      else if (S_ISREG (fsentry->header.mode))
2512
      {
2513
         free (fsentry->u.file.path);
2514
         free (fsentry->u.file.UNSAVED_databuf);
2515
      }
2516
      else if (S_ISFIFO (fsentry->header.mode))
2517
         free (fsentry->u.device.path);
14 pmbaty 2518
   }
2519
 
16 pmbaty 2520
   // and exit with a success code
2521
   LOG_INFO ("Success");
2522
   exit (0);
14 pmbaty 2523
}