Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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