Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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