Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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