Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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