Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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