Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 41 | Rev 43 | 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
42 pmbaty 894
   for (array_index = 0; array_index < global_envp.count; array_index++)
895
      ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, global_envp.args[array_index], strlen (global_envp.args[array_index]) + 1)); // append string including NUL terminator
41 pmbaty 896
   for (array_index = 0; array_index < exe_env->count; array_index++)
897
      ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, exe_env->args[array_index], strlen (exe_env->args[array_index]) + 1)); // append string including NUL terminator
898
   ((bootargs_entry_t *) bootargs_buffer.bytes)->size_hi = (uint8_t) ((bootargs_buffer.size >> 8) & 0xff);
899
   ((bootargs_entry_t *) bootargs_buffer.bytes)->size_lo = (uint8_t) ((bootargs_buffer.size >> 0) & 0xff);
900
   *bootargs_offset = (size_t) exeargs_location - (size_t) out_buffer->bytes; // compute the boot args offset
901
   ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (out_buffer, *bootargs_offset, &bootargs_buffer));
902
   Buffer_Forget (&bootargs_buffer); // release the boot args buffer once it's written
903
 
904
   return; // either we made it through, or we're dead
905
}
906
 
907
 
19 pmbaty 908
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 909
{
16 pmbaty 910
   static thread_local char *candidate_pathname = NULL;
21 pmbaty 911
   static thread_local parms_t default_parms = { 0 };
22 pmbaty 912
   static thread_local stringarray_t aps_partnames = { NULL, 0 };
1 pmbaty 913
   static int inode_count = 0; // will be preincremented each time this function is called
914
 
22 pmbaty 915
   typedef struct scriptcmd_s
916
   {
24 pmbaty 917
      char *argv0;
22 pmbaty 918
      int cpu_number;
919
      bool is_external;
920
      int priority;
921
      int sched_policy;
922
      int aps_partindex;
923
      bool is_session_leader;
924
      bool is_background_task;
925
      bool has_debug_flag;
926
   } scriptcmd_t;
927
 
928
   scriptcmd_t default_scriptcmd_params = { NULL, -1, false, -1, -1, -1, false, false, false };
929
   scriptcmd_t current_scriptcmd_params = { 0 };
20 pmbaty 930
   stringarray_t global_argv = { NULL, 0 };
931
   stringarray_t line_argv = { NULL, 0 };
932
   stringarray_t line_envp = { NULL, 0 };
41 pmbaty 933
//   stringarray_t linker_argv = { NULL, 0 };
18 pmbaty 934
   const char *stored_pathname_without_leading_slash;
7 pmbaty 935
   const char *original_stored_pathname = NULL;
34 pmbaty 936
   const char *filename_bit;
20 pmbaty 937
   buffer_t current_line;
22 pmbaty 938
   buffer_t compiled_script;
939
   buffer_t compiled_scriptline;
16 pmbaty 940
   buffer_t *shstrtab = NULL;
7 pmbaty 941
   const char *canonical_dylib_name;
942
   const char *dynamic_strings; // strings table of the ".dynamic" section
943
   const char *last_dirsep;
20 pmbaty 944
   size_t array_index;
945
   size_t line_index;
18 pmbaty 946
   size_t fsentry_index;
34 pmbaty 947
   size_t pathbit_len;
22 pmbaty 948
   size_t wait_time;
7 pmbaty 949
   char *resolved_pathname;
20 pmbaty 950
   char *linebit_start;
951
   char *write_ptr;
952
   char *read_ptr;
953
   char *token;
954
   char *value;
955
   char *ctx;
1 pmbaty 956
   void *reallocated_ptr;
7 pmbaty 957
   void *old_data;
20 pmbaty 958
   bool is_quoted_context;
959
   bool is_end_of_line;
1 pmbaty 960
   struct stat stat_buf;
961
   fsentry_t *fsentry;
19 pmbaty 962
   int retval;
1 pmbaty 963
 
16 pmbaty 964
   // initial allocation (per thread)
965
   if (candidate_pathname == NULL)
966
   {
967
      candidate_pathname = malloc (MAXPATHLEN);
968
      ASSERT_WITH_ERRNO (candidate_pathname);
969
   }
970
 
1 pmbaty 971
   if (S_ISDIR (entry_parms->st_mode)) // are we storing a directory ?
972
   {
24 pmbaty 973
      if ((buildhost_pathname != NULL) && (buildhost_pathname[0] != 0)) // was a source file pathname supplied ?
21 pmbaty 974
      {
975
         memcpy (&default_parms, entry_parms, sizeof (parms_t)); // apply current entry parameters when including a directory recursively
976
         add_directory_contents_recursively (fsentries, fsentry_count, buildhost_pathname, strlen (buildhost_pathname), &default_parms); // if so, add this diretory contents recursively
977
      }
15 pmbaty 978
      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 979
   }
21 pmbaty 980
   else if (S_ISLNK (entry_parms->st_mode)) // else are we storing a symbolic link ?
1 pmbaty 981
   {
21 pmbaty 982
      // do we already know the data for this data blob ?
983
      if (entry_parms->data.bytes != NULL)
984
      {
985
         entry_parms->mtime = entry_parms->mtime_for_inline_files; // if so, set it a mtime equal to the mtime to use for inline files
986
         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);
987
      }
988
      else if (buildhost_pathname != NULL) // else was a source file pathname supplied ?
989
      {
990
         entry_parms->data.bytes = malloc (MAXPATHLEN); // allocate enough space for symlink data
991
         ASSERT_WITH_ERRNO (entry_parms->data.bytes);
38 pmbaty 992
         retval = readlink (buildhost_pathname, (char *) entry_parms->data.bytes, MAXPATHLEN); // read symlink contents
21 pmbaty 993
         ASSERT_WITH_ERRNO (retval > 0);
994
         entry_parms->data.size = retval; // save symlink target length
995
      }
996
      else
997
         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.");
998
   }
999
   else if (S_ISFIFO (entry_parms->st_mode)) // else are we storing a FIFO ?
1000
   {
38 pmbaty 1001
      if ((entry_parms->data.bytes == NULL) || (strchr ((char *) entry_parms->data.bytes, ':') == NULL))
21 pmbaty 1002
         DIE_WITH_EXITCODE (1, "device entry \"%s\" malformed (no 'dev:rdev' pair)", stored_pathname);
1003
      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);
1004
   }
1005
   else // necessarily a regular file (either S_IFREG is specified, or st_mode is zero)
1006
   {
1007
      entry_parms->st_mode |= S_IFREG; // make this explicit
35 pmbaty 1008
      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 1009
 
35 pmbaty 1010
      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.");
1011
 
1012
      // 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
1013
      if ((entry_parms->data.bytes == NULL) && (buildhost_pathname != NULL))
1 pmbaty 1014
      {
35 pmbaty 1015
         resolved_pathname = resolve_pathname (buildhost_pathname, entry_parms->search); // locate the file
1016
         if (resolved_pathname == NULL)
1017
         {
1018
            if (entry_parms->should_allow_nonexistent_files)
1019
            {
1020
               LOG_WARNING ("filesystem entry \"%s\" specified in \"%s\" line %d not found on build host: ignoring", buildhost_pathname, buildfile_pathname, lineno);
1021
               return; // if we're allowed to continue when a file to add doesn't exist, do so, else die with an error message
1022
            }
1023
            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));
1024
         }
1025
         if (!Buffer_ReadFromFile (&entry_parms->data, resolved_pathname))
1026
            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));
1027
         stat (resolved_pathname, &stat_buf); // can't fail, since we could read it
1028
         if (entry_parms->mtime == UINT32_MAX)
1029
            entry_parms->mtime = (uint32_t) stat_buf.st_mtime; // now we know which mtime to set this file
1030
      }
1031
 
1032
      // is it the bootstrap file [startup=], or a "compiled" bootscript [+script] ?
1033
      if (entry_parms->is_bootstrap_file) // [startup=...]
1034
      {
20 pmbaty 1035
         // parse each line of contents
1036
         ASSERT (entry_parms->data.size > 0, "kernel specification without inline contents");
15 pmbaty 1037
 
20 pmbaty 1038
         // parse buffer (non-destructively) line after line
1039
         Buffer_Initialize (&current_line);
1040
         for (line_index = 0; Buffer_GetNthLine (&entry_parms->data, line_index, &current_line); line_index++)
15 pmbaty 1041
         {
38 pmbaty 1042
            read_ptr = (char *) current_line.bytes;
20 pmbaty 1043
            while (isspace (*read_ptr))
1044
               read_ptr++; // skip leading spaces
1045
            if ((*read_ptr == '#') || (*read_ptr == 0))
15 pmbaty 1046
               continue; // skip comments and empty lines
1047
 
1048
            // format of a line: [attributes] [env assignation] [...] [executable] [arg] [...] [comment]
1049
            // example: "[uid=0 gid=0 perms=0700] CONFIG_PATH=/proc/boot:/etc procnto-smp-instr -v -mr -d 0777 -u 0777"
1050
 
20 pmbaty 1051
            LOG_DEBUG ("parsing line: %s", read_ptr);
1052
 
15 pmbaty 1053
            // does this line start with an attribute block ?
20 pmbaty 1054
            if (*read_ptr == '[')
15 pmbaty 1055
            {
20 pmbaty 1056
               read_ptr++; // skip the leading square bracket
1057
               linebit_start = read_ptr; // remember where it starts
15 pmbaty 1058
               is_quoted_context = false; // reach the next unescaped closing square bracket that is not between quotes
20 pmbaty 1059
               while ((*read_ptr != 0) && !((*read_ptr == ']') && (read_ptr[-1] != '\\') && !is_quoted_context))
15 pmbaty 1060
               {
20 pmbaty 1061
                  if (*read_ptr == '"')
15 pmbaty 1062
                     is_quoted_context ^= true; // remember when we're between quotes
20 pmbaty 1063
                  else if (!is_quoted_context && (*read_ptr == ' '))
1064
                     *read_ptr = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
1065
                  read_ptr++; // reach the next unescaped closing square bracket
15 pmbaty 1066
               }
20 pmbaty 1067
               if (*read_ptr != ']')
15 pmbaty 1068
               {
22 pmbaty 1069
                  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 1070
                  continue; // invalid attribute block, skip line
1071
               }
20 pmbaty 1072
               is_end_of_line = (*read_ptr == 0); // see if we're at the end of line already
1073
               *read_ptr = 0; // end the attribute block in all cases so that it is a parsable C string
15 pmbaty 1074
 
1075
               // now parse the attribute tokens (NOTE: THE LIST OF ALLOWED ATTRIBUTES HERE IS NOT DOCUMENTED)
16 pmbaty 1076
               token = strtok_r (linebit_start, RECORD_SEP, &ctx);
15 pmbaty 1077
               while (token != NULL)
1078
               {
1079
                  #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
20 pmbaty 1080
                  if (false) {}
17 pmbaty 1081
                  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 1082
                  else if (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms->uid     = (int) read_integer (value, 10); }
1083
                  else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms->gid     = (int) read_integer (value, 10); }
1084
                  else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms->perms   = (int) read_integer (value, 8); }
15 pmbaty 1085
                  else if (strcmp (token, "+followlink") == 0) entry_parms->should_follow_symlinks = true;
1086
                  else if (strcmp (token, "-followlink") == 0) entry_parms->should_follow_symlinks = false;
1087
                  else if (strcmp (token, "+keeplinked") == 0) entry_parms->should_keep_ld_output = true;
1088
                  else if (strcmp (token, "-keeplinked") == 0) entry_parms->should_keep_ld_output = false;
22 pmbaty 1089
                  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 1090
                  #undef REACH_TOKEN_VALUE
16 pmbaty 1091
                  token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
15 pmbaty 1092
               }
1093
 
20 pmbaty 1094
               if (is_end_of_line)
1095
                  continue; // if end of line was reached, proceed to the next line
1096
               else
1097
                  read_ptr++; // else reach the next character (after the NUL split) and continue processing the same line
15 pmbaty 1098
            } // end of "this line starts with an attributes block"
1099
 
20 pmbaty 1100
            // at this point we are past the attributes block
1101
 
1102
            // reset contextual argv/envp arrays
1103
            line_argv.args = NULL;
1104
            line_argv.count = 0;
1105
            line_envp.args = NULL;
1106
            line_envp.count = 0;
1107
 
1108
            // now read each word (or quoted group of words), unescaping escaped characters
1109
            while (*read_ptr != 0)
15 pmbaty 1110
            {
20 pmbaty 1111
               while ((*read_ptr != 0) && isspace (*read_ptr))
1112
                  read_ptr++; // skip intermediate spaces and reach the next word
1113
 
1114
               if (*read_ptr == '#')
1115
                  break; // if the rest of the line is commented out, stop parsing it and proceed to the next line
1116
 
1117
               linebit_start = read_ptr; // remember the word (or quoted group of words) starts here
1118
               write_ptr = read_ptr;
1119
               is_quoted_context = (*read_ptr == '"'); // see if we're entering a quoted context or not
15 pmbaty 1120
               if (is_quoted_context)
20 pmbaty 1121
                  read_ptr++; // skip a possible initial quote in the word
22 pmbaty 1122
               while ((*read_ptr != 0) && ((!is_quoted_context && !isspace (*read_ptr)) || (is_quoted_context && (*read_ptr != '"'))))
15 pmbaty 1123
               {
20 pmbaty 1124
                  if (*read_ptr == '\\')
1125
                     read_ptr++; // unescape characters that are escaped with '\' by advancing the read pointer
1126
                  *write_ptr++ = *read_ptr++; // recopy characters as we read them
15 pmbaty 1127
               }
20 pmbaty 1128
               is_end_of_line = (*read_ptr == 0); // see if we're at the end of line already
1129
               *write_ptr = 0; // stop the rewritten string here
15 pmbaty 1130
 
20 pmbaty 1131
               // 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.
1132
 
1133
               if ((strchr (linebit_start, '=') != NULL) && (line_argv.count == 0)) // is it an assignation AND have we not started constructing argv yet?
15 pmbaty 1134
               {
20 pmbaty 1135
                  STRINGARRAY_PUSH (&line_envp, linebit_start); // linebit_start is of the form "NAME=VALUE": it's an environment variable assignation
1136
                  LOG_DEBUG ("collected envp: [%s]", linebit_start);
1137
               }
1138
               else // it's an executable argument (argv)
1139
               {
1140
                  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
1141
                  LOG_DEBUG ("collected argv: [%s]", linebit_start);
1142
               }
15 pmbaty 1143
 
20 pmbaty 1144
               if (!is_end_of_line)
1145
                  read_ptr++; // if we haven't reach the end of the line yet, advance to the next character (after the NUL split)
1146
            } // end while (*read_ptr != 0)
15 pmbaty 1147
 
20 pmbaty 1148
            // we finished parsing the line
15 pmbaty 1149
 
20 pmbaty 1150
            // did we fill an executable argv? As per QNX docs, the first executable must be startup-*, the last executable must be procnto.
1151
            if (line_argv.count > 0)
1152
            {
1153
               if (startup_argv.args == NULL)
1154
               {
1155
                  startup_argv.args = line_argv.args; // relocate these pointers to the right place
1156
                  startup_argv.count = line_argv.count;
1157
                  startup_envp.args = line_envp.args; // relocate these pointers to the right place
1158
                  startup_envp.count = line_envp.count;
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;
1168
               }
1169
               line_argv.args = NULL; // void the line_argv array so as to not free it as we stole its args pointers
1170
               line_argv.count = 0;
1171
               line_envp.args = NULL; // void the line_envp array so as to not free it as we stole its args pointers
1172
               line_envp.count = 0;
1173
            }
1174
            else // this line contained no executable invokation, so stack up its envp assignations into the global envp array
1175
               for (array_index = 0; array_index < line_envp.count; array_index++)
1176
                  STRINGARRAY_PUSH (&global_envp, line_envp.args[array_index]);
15 pmbaty 1177
 
20 pmbaty 1178
            // release the contextual argv/envp arrays
1179
            STRINGARRAY_FREE (&line_argv);
1180
            STRINGARRAY_FREE (&line_envp);
15 pmbaty 1181
 
20 pmbaty 1182
         } // end for (line_index = 0; Buffer_GetNthLine (&entry_parms->data, line_index, &current_line); line_index++)
22 pmbaty 1183
         Buffer_Forget (&entry_parms->data); // free the inline specification once it's parsed
15 pmbaty 1184
 
20 pmbaty 1185
         ASSERT (startup_argv.args && startup_argv.args[0] && *startup_argv.args[0], "the QNX startup executable (startup-*) is missing in this bootstrap inline specification");
1186
         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 1187
 
1188
         // now we know which startup and procnto executables to use
20 pmbaty 1189
         LOG_DEBUG ("Startup: %s", startup_argv.args[0]);
1190
         LOG_DEBUG ("Kernel: %s",  procnto_argv.args[0]);
15 pmbaty 1191
 
41 pmbaty 1192
         // prelink procnto, map its .text segment at the right virtual address for the IFS and stamp in its executable arguments
1193
         // then store the result in this entry's data blob
1194
         prelink (entry_parms->search, // binary executable search path
1195
                  (boot_type == BOOTTYPE_UEFI ? ".text=0xffff800000002000" : ".text=0xffff800000001000"), // FIXME: wild assumption!
1196
                  &procnto_argv, &procnto_envp, // executable arguments (including its name) and environment to stamp in it
1197
                  entry_parms->should_keep_ld_output, // whether to keep the linker-produced output file
1198
                  &entry_parms->data, // buffer where to save the output
1199
                  &procnto_bootargs_offset); // where to save the offset of the boot arguments
15 pmbaty 1200
 
41 pmbaty 1201
         // strip this prelinked ELF file from all the sections we don't need
1202
         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 1203
 
20 pmbaty 1204
         sprintf_s (candidate_pathname, MAXPATHLEN, "%s/%s", (entry_parms->prefix != NULL ? entry_parms->prefix : ""), procnto_argv.args[0]); // fix the entry name
1205
         stored_pathname = candidate_pathname;
1206
 
1207
         entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // mark this inode as a preprocessed *bootstrap* ELF file
1208
         entry_parms->st_mode = S_IFREG | entry_parms->perms; // procnto is a regular file
1209
         image_kernel_ino = entry_parms->extra_ino_flags | (inode_count + 1);
1210
 
41 pmbaty 1211
         memcpy (&startup_entry_parms, entry_parms, sizeof (parms_t)); // backup the startup entry parameters
1212
 
20 pmbaty 1213
         STRINGARRAY_FREE (&procnto_argv); // release procnto's argv array
1214
         STRINGARRAY_FREE (&procnto_envp); // release procnto's envp array
22 pmbaty 1215
         //STRINGARRAY_FREE (&global_envp); // DO NOT release the global envp array. It is inherited by the boot scripts.
41 pmbaty 1216
 
22 pmbaty 1217
      } // end of "is bootstrap file"
35 pmbaty 1218
      else if (entry_parms->is_compiled_bootscript) // [+script]
22 pmbaty 1219
      {
7 pmbaty 1220
         image_bootscript_ino = inode_count + 1; // save boot script inode number for image header
22 pmbaty 1221
         Buffer_Initialize (&compiled_script);
2 pmbaty 1222
 
22 pmbaty 1223
         // parse buffer (non-destructively) line after line
1224
         Buffer_Initialize (&current_line);
1225
         for (line_index = 0; Buffer_GetNthLine (&entry_parms->data, line_index, &current_line); line_index++)
1226
         {
38 pmbaty 1227
            read_ptr = (char *) current_line.bytes;
22 pmbaty 1228
            while (isspace (*read_ptr))
1229
               read_ptr++; // skip leading spaces
1230
            if ((*read_ptr == '#') || (*read_ptr == 0))
1231
               continue; // skip comments and empty lines
1232
 
1233
            // format of a line: [attributes] [env assignation] [...] [executable] [arg] [...] [&] [comment]
1234
            // example: "[pri=20f] devc-con -n9 &"
1235
 
1236
            LOG_DEBUG ("parsing line: %s", read_ptr);
1237
            Buffer_Initialize (&compiled_scriptline);
1238
            memcpy (&current_scriptcmd_params, &default_scriptcmd_params, sizeof (default_scriptcmd_params));
1239
 
1240
            // does this line start with an attribute block ?
1241
            if (*read_ptr == '[')
1242
            {
1243
               read_ptr++; // skip the leading square bracket
1244
               linebit_start = read_ptr; // remember where it starts
1245
               is_quoted_context = false; // reach the next unescaped closing square bracket that is not between quotes
1246
               while ((*read_ptr != 0) && !((*read_ptr == ']') && (read_ptr[-1] != '\\') && !is_quoted_context))
1247
               {
1248
                  if (*read_ptr == '"')
1249
                     is_quoted_context ^= true; // remember when we're between quotes
1250
                  else if (!is_quoted_context && (*read_ptr == ' '))
1251
                     *read_ptr = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
1252
                  read_ptr++; // reach the next unescaped closing square bracket
1253
               }
1254
               if (*read_ptr != ']')
1255
               {
1256
                  LOG ("warning", 0, "syntax error in \"%s\" line %zd of inline document '%s': unterminated attributes block (skipping)", buildfile_pathname, 1 + line_index, stored_pathname);
1257
                  continue; // invalid attribute block, skip line
1258
               }
1259
               is_end_of_line = (*read_ptr == 0); // see if we're at the end of line already
1260
               *read_ptr = 0; // end the attribute block in all cases so that it is a parsable C string
1261
 
1262
               // now parse the attribute tokens
1263
               token = strtok_r (linebit_start, RECORD_SEP, &ctx);
1264
               while (token != NULL)
1265
               {
1266
                  #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
1267
                  if (false) {}
1268
                  else if (strncmp (token, "argv0=",      6) == 0) { REACH_TOKEN_VALUE (); current_scriptcmd_params.argv0      = value; } // NOTE: stolen pointer. Do not free.
1269
                  else if (strncmp (token, "cpu=",        4) == 0) { REACH_TOKEN_VALUE (); current_scriptcmd_params.cpu_number = (int) atoi (value); }
1270
                  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); }
1271
                  else if (strncmp (token, "sched_aps=", 10) == 0) { REACH_TOKEN_VALUE ();
1272
                     for (array_index = 0; array_index < aps_partnames.count; array_index++) if (strcmp (aps_partnames.args[array_index], value) == 0) break;
1273
                     if (array_index == aps_partnames.count)
1274
                        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%)
1275
                     current_scriptcmd_params.aps_partindex = (int) array_index;
1276
                  }
1277
                  else if (strcmp (token, "+external") == 0) current_scriptcmd_params.is_external = true;
1278
                  else if (strcmp (token, "-external") == 0) current_scriptcmd_params.is_external = false;
1279
                  else if (strcmp (token, "+session")  == 0) current_scriptcmd_params.is_session_leader = true;
1280
                  else if (strcmp (token, "-session")  == 0) current_scriptcmd_params.is_session_leader = false;
1281
                  else if (strcmp (token, "+debug")    == 0) current_scriptcmd_params.has_debug_flag = true;
1282
                  else if (strcmp (token, "-debug")    == 0) current_scriptcmd_params.has_debug_flag = false;
1283
                  else LOG_WARNING ("unimplemented boot script modifier in \"%s\" line %zd of inline document '%s': '%s'", buildfile_pathname, 1 + line_index, stored_pathname, token);
1284
                  #undef REACH_TOKEN_VALUE
1285
                  token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
1286
               }
1287
 
1288
               if (is_end_of_line)
1289
                  continue; // if end of line was reached, proceed to the next line
1290
               else
1291
                  read_ptr++; // else reach the next character (after the NUL split) and continue processing the same line
1292
            } // end of "this line starts with an attributes block"
1293
 
1294
            // at this point we are past the attributes block
1295
 
1296
            // reset contextual argv/envp arrays
1297
            line_argv.args = NULL;
1298
            line_argv.count = 0;
1299
            line_envp.args = NULL;
1300
            line_envp.count = 0;
1301
 
1302
            // now read each word (or quoted group of words), unescaping escaped characters
1303
            while (*read_ptr != 0)
1304
            {
1305
               while ((*read_ptr != 0) && isspace (*read_ptr))
1306
                  read_ptr++; // skip intermediate spaces and reach the next word
1307
 
1308
               if (*read_ptr == '#')
1309
                  break; // if the rest of the line is commented out, stop parsing it and proceed to the next line
1310
 
1311
               linebit_start = read_ptr; // remember the word (or quoted group of words) starts here
1312
               write_ptr = read_ptr;
1313
               is_quoted_context = (*read_ptr == '"'); // see if we're entering a quoted context or not
1314
               if (is_quoted_context)
1315
                  read_ptr++; // skip a possible initial quote in the word
1316
               while ((*read_ptr != 0) && ((!is_quoted_context && !isspace (*read_ptr)) || (is_quoted_context && (*read_ptr != '"'))))
1317
               {
1318
                  if (*read_ptr == '\\')
1319
                     read_ptr++; // unescape characters that are escaped with '\' by advancing the read pointer
1320
                  *write_ptr++ = *read_ptr++; // recopy characters as we read them
1321
               }
1322
               is_end_of_line = (*read_ptr == 0); // see if we're at the end of line already
1323
               *write_ptr = 0; // stop the rewritten string here
1324
 
1325
               // 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.
1326
               STRINGARRAY_PUSH (&line_argv, linebit_start);
1327
               LOG_DEBUG ("collected bootscript argv: [%s]", linebit_start);
1328
 
1329
               if (!is_end_of_line)
1330
                  read_ptr++; // if we haven't reach the end of the line yet, advance to the next character (after the NUL split)
1331
            } // end while (*read_ptr != 0)
1332
 
1333
            // we finished parsing the line
1334
 
1335
            // did we fill an executable argv? As per QNX docs, the first executable must be startup-*, the last executable must be procnto.
1336
            if (line_argv.count > 0)
1337
            {
1338
               // is it one of the few builtin commands ?
1339
               if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "waitfor") == 0))
1340
               {
1341
                  if (line_argv.count < 2)
1342
                     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);
1343
 
1344
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_WAITFOR "\x00", 4)); // size as u16LE, type, spare
1345
                  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)
1346
                  if (wait_time > 0xffff)
1347
                     wait_time = 0xffff;
1348
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 4, (wait_time >> 0) & 0xff)); // wait time lo
1349
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 5, (wait_time >> 8) & 0xff)); // wait time hi
1350
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[1], strlen (line_argv.args[1]) + 1));
1351
               }
1352
               else if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "reopen") == 0))
1353
               {
1354
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_REOPEN "\x00", 4)); // size as u16LE, type, spare
1355
                  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)
1356
                  if (wait_time > 0xffff)
1357
                     wait_time = 0xffff;
1358
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 4, (wait_time >> 0) & 0xff)); // wait time lo
1359
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 5, (wait_time >> 8) & 0xff)); // wait time hi
1360
                  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));
1361
               }
1362
               else if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "display_msg") == 0))
1363
               {
1364
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_DISPLAY_MSG "\x00", 4)); // size as u16LE, type, spare
1365
                  for (array_index = 1; array_index < line_argv.count; array_index++)
1366
                  {
1367
                     if (array_index > 1)
1368
                        ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_scriptline, " ")); // separate each arg with a space
1369
                     ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[array_index], strlen (line_argv.args[array_index])));
1370
                  }
1371
                  ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_scriptline, "\n\0")); // don't forget to append a newline to the message printed
1372
               }
1373
               else if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "procmgr_symlink") == 0))
1374
               {
1375
                  if (line_argv.count < 3)
1376
                     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);
1377
 
1378
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_PROCMGR_SYMLINK "\x00", 4)); // size as u16LE, type, spare
1379
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[1], strlen (line_argv.args[1]) + 1));
1380
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[2], strlen (line_argv.args[2]) + 1));
1381
               }
1382
               else if (!current_scriptcmd_params.is_external && (strcmp (line_argv.args[0], "sched_aps") == 0))
1383
               {
1384
                  token = (line_argv.count > 1 ? line_argv.args[1] : "System");
1385
                  if ((strlen (token) > 15) || (strchr (token, '/') != NULL))
1386
                     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%)
1387
                  for (array_index = 0; array_index < aps_partnames.count; array_index++)
1388
                     if (strcmp (aps_partnames.args[array_index], token) == 0)
1389
                        break; // find the APS partition ID in the global APS partition names table
1390
                  if (array_index == aps_partnames.count)
1391
                     STRINGARRAY_PUSH (&aps_partnames, token); // if not found, add a new partition name to the table
1392
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_EXTSCHED_APS "\x00", 4)); // size as u16LE, type, spare
1393
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 4, 0)); // parent (system partition)
1394
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 5, (line_argv.count > 2 ? (uint8_t) atoi (line_argv.args[2]) : 0))); // budget
1395
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 6, ((line_argv.count > 3 ? (uint8_t) atoi (line_argv.args[3]) : 0) >> 0) & 0xff)); // critical lo
1396
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 7, ((line_argv.count > 3 ? (uint8_t) atoi (line_argv.args[3]) : 0) >> 8) & 0xff)); // critical hi
1397
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 8, (uint8_t) array_index)); // APS partition ID
1398
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, token, strlen (token) + 1)); // partition name
1399
               }
1400
               else // not a builtin, which means it is an external command
1401
               {
1402
                  if (strcmp (line_argv.args[line_argv.count - 1], "&") == 0) // is the last argument an ampersand (fork sign) on its own ? (variant 1)
1403
                  {
1404
                     current_scriptcmd_params.is_background_task = true; // remember this is a background task
1405
                     free (line_argv.args[line_argv.count - 1]); // prevent leaking the last arg
1406
                     line_argv.count--; // and adjust the arg count
1407
                  }
1408
                  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)
1409
                  {
1410
                     current_scriptcmd_params.is_background_task = true; // remember this is a background task
1411
                     *token = 0; // and chop off the ampersand from that arg
1412
                  }
1413
 
1414
                  ASSERT_WITH_ERRNO (Buffer_InitWithData (&compiled_scriptline, "##" SCRIPTCMD_TYPE_EXTERNAL "\x00", 4)); // size as u16LE, type, spare
1415
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 4, (current_scriptcmd_params.cpu_number != -1 ? (uint8_t) current_scriptcmd_params.cpu_number : 0))); // CPU
1416
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 5, (current_scriptcmd_params.aps_partindex != -1 ? SCRIPTCMD_FLAG_EXTSCHED   : 0)
1417
                                                                                | (current_scriptcmd_params.is_session_leader   ? SCRIPTCMD_FLAG_SESSION    : 0)
1418
                                                                                | (current_scriptcmd_params.sched_policy  != -1 ? SCRIPTCMD_FLAG_SCHED_SET  : 0)
1419
                                                                                | (current_scriptcmd_params.cpu_number    != -1 ? SCRIPTCMD_FLAG_CPU_SET    : 0)
1420
                                                                                | (current_scriptcmd_params.is_background_task  ? SCRIPTCMD_FLAG_BACKGROUND : 0)
1421
                                                                                | (current_scriptcmd_params.has_debug_flag      ? SCRIPTCMD_FLAG_KDEBUG     : 0))); // flags
1422
                  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
1423
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 7, 0)); // reserved
1424
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 8, (current_scriptcmd_params.sched_policy != -1 ? current_scriptcmd_params.sched_policy : 0))); // scheduling policy
1425
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 9, (current_scriptcmd_params.priority != -1 ? current_scriptcmd_params.priority : 0))); // scheduling priority
1426
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 10, (uint8_t) line_argv.count)); // argc
1427
                  ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 11, (uint8_t) global_envp.count)); // envc
1428
                  ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[0], strlen (line_argv.args[0]) + 1)); // executable
34 pmbaty 1429
                  if (current_scriptcmd_params.argv0 != NULL)
1430
                     ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, current_scriptcmd_params.argv0, strlen (current_scriptcmd_params.argv0) + 1)); // argv[0] -- explicit value from attribute
1431
                  else
1432
                  {
1433
                     filename_bit = strrchr (line_argv.args[0], '/'); // argv[0] has an implicit value: look where the filename starts
1434
                     filename_bit = (filename_bit != NULL ? filename_bit + 1 : line_argv.args[0]);
1435
                     ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, filename_bit, strlen (filename_bit) + 1)); // argv[0] -- store just the filename
1436
                  }
22 pmbaty 1437
                  for (array_index = 1; array_index < line_argv.count; array_index++)
1438
                     ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, line_argv.args[array_index], strlen (line_argv.args[array_index]) + 1)); // argv[n]
1439
                  for (array_index = 0; array_index < global_envp.count; array_index++)
1440
                     ASSERT_WITH_ERRNO (Buffer_Append (&compiled_scriptline, global_envp.args[array_index], strlen (global_envp.args[array_index]) + 1)); // envp[n]
1441
               }
1442
               ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&compiled_scriptline, ROUND_TO_UPPER_MULTIPLE (compiled_scriptline.size, 4))); // pad compiled command buffer to upper 32-bit multiple
1443
 
1444
               // fix the size of this compiled boot script command
1445
               ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 0, (compiled_scriptline.size >> 0) & 0xff)); // size lo
1446
               ASSERT_WITH_ERRNO (Buffer_WriteInt8At (&compiled_scriptline, 1, (compiled_scriptline.size >> 8) & 0xff)); // size hi
1447
 
1448
               // now concatenate this newly compiled boot script line to the compiled boot script buffer
1449
               ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&compiled_script, &compiled_scriptline));
1450
               Buffer_Forget (&compiled_scriptline);
1451
            }
1452
            else // this line contained no executable invokation, so make the parameters that changed the default ones
1453
            {
1454
               #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (current_scriptcmd_params.attr != default_scriptcmd_params.attr) { \
1455
                     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); \
1456
                     default_scriptcmd_params.attr = current_scriptcmd_params.attr; \
1457
                  } } while (0)
1458
               #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))) { \
1459
                  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 1460
                     if (default_scriptcmd_params.attr != NULL) free (default_scriptcmd_params.attr); \
1461
                     default_scriptcmd_params.attr = strdup (current_scriptcmd_params.attr); \
1462
                     ASSERT_WITH_ERRNO (default_scriptcmd_params.attr != NULL); \
22 pmbaty 1463
                     default_scriptcmd_params.attr = current_scriptcmd_params.attr; \
1464
                  } } while (0)
1465
               APPLY_DEFAULT_ATTR_STR (argv0,                          "executable name",                 "\"%s\"");
1466
               APPLY_DEFAULT_ATTR_NUM (cpu_number,                     "CPU mask",                        "0%o");
1467
               APPLY_DEFAULT_ATTR_NUM (is_external,                    "external command flag",           "0%o");
1468
               APPLY_DEFAULT_ATTR_NUM (priority,                       "scheduling priority",             "0%o");
1469
               APPLY_DEFAULT_ATTR_NUM (sched_policy,                   "scheduling policy",               "0%o");
1470
               APPLY_DEFAULT_ATTR_NUM (aps_partindex,                  "APS partition index",             "0%o");
1471
               APPLY_DEFAULT_ATTR_NUM (is_session_leader,              "session leader flag",             "0%o");
1472
               APPLY_DEFAULT_ATTR_NUM (is_background_task,             "background task flag",            "0%o");
1473
               APPLY_DEFAULT_ATTR_NUM (has_debug_flag,                 "debug flag",                      "0%o");
1474
               #undef APPLY_DEFAULT_ATTR_STR
1475
               #undef APPLY_DEFAULT_ATTR_NUM
1476
            }
1477
 
1478
            // release the contextual argv/envp arrays
1479
            STRINGARRAY_FREE (&line_argv);
1480
            STRINGARRAY_FREE (&line_envp);
1481
 
1482
         } // end for (line_index = 0; Buffer_GetNthLine (&entry_parms->data, line_index, &current_line); line_index++)
1483
         Buffer_Forget (&entry_parms->data); // free the inline specification once it's parsed
1484
 
1485
         ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_script, "\x00\x00\x00\x00")); // terminate the compiled boot script with a 4-byte trailer
1486
         entry_parms->data.bytes = compiled_script.bytes; // and steal the compiled boot script buffer
1487
         entry_parms->data.size = compiled_script.size;
1488
      } // end of "is compiled bootscript"
1489
 
35 pmbaty 1490
      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 1491
 
9 pmbaty 1492
      // is the file we're storing an ELF file ?
16 pmbaty 1493
      #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
1494
      if ((entry_parms->data.size > 52) // file is big enough to contain an ELF header
1495
          && (memcmp (ELF_GET_STRING (ELFHDR, ELFHDR, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic
7 pmbaty 1496
      {
35 pmbaty 1497
         if ((entry_parms->st_mode & 0111) == 0)
1498
            entry_parms->st_mode |= 0111; // add +x permissions to ELF entries if they have none (undocumented mkifs behaviour)
1499
 
9 pmbaty 1500
         // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ?
16 pmbaty 1501
         if ((ELF_GET_NUMERIC (ELFHDR, ELFHDR, type) == ELF_TYPE_DYNAMICLIB) && entry_parms->should_autosymlink_dylib)
7 pmbaty 1502
         {
9 pmbaty 1503
            // locate the sections we need (the dynamic section and its strings table)
16 pmbaty 1504
            const elf_section_header_t *shdr_dynamic = elf_get_section_header_by_name (ELFHDR, ".dynamic");
1505
            const elf_section_header_t *shdr_dynstr = elf_get_section_header_by_name (ELFHDR, ".dynstr");
7 pmbaty 1506
 
9 pmbaty 1507
            // make sure we have both the dynamic section header and its own strings table header
1508
            if ((shdr_dynamic != NULL) && (shdr_dynstr != NULL))
1509
            {
16 pmbaty 1510
               dynamic_strings = (char *) &entry_parms->data.bytes[ELF_GET_NUMERIC (ELFHDR, shdr_dynstr, file_offset)]; // quick access to dynamic sections strings table
7 pmbaty 1511
 
9 pmbaty 1512
               // walk through the dynamic section, look for the DT_SONAME entry
16 pmbaty 1513
               canonical_dylib_name = NULL; // assume none until told otherwise
1514
               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)];
1515
                    (ELF_GET_NUMERIC (ELFHDR, dynamic_entry, tag) != ELF_DT_NULL);
1516
                    dynamic_entry = (elf_dynamic_section_entry_t *) ((uint8_t *) dynamic_entry + ELF_STRUCT_SIZE (ELFHDR, dynamic_entry)))
1517
                  if (ELF_GET_NUMERIC (ELFHDR, dynamic_entry, tag) == ELF_DT_SONAME)
9 pmbaty 1518
                  {
16 pmbaty 1519
                     canonical_dylib_name = dynamic_strings + ELF_GET_NUMERIC (ELFHDR, dynamic_entry, value);
9 pmbaty 1520
                     break;
1521
                  }
1522
 
34 pmbaty 1523
               // do we have the canonical dylib name AND does it differ from the name under which we'll be storing this dylib ?
1524
               filename_bit = strrchr (stored_pathname, '/');
1525
               if (filename_bit != NULL)
7 pmbaty 1526
               {
34 pmbaty 1527
                  filename_bit++;
1528
                  pathbit_len = filename_bit - stored_pathname;
1529
               }
1530
               else
1531
               {
1532
                  filename_bit = stored_pathname;
1533
                  pathbit_len = 0;
1534
               }
1535
               if ((canonical_dylib_name != NULL) && (canonical_dylib_name[0] != 0) && (strcmp (canonical_dylib_name, filename_bit) != 0))
1536
               {
1537
                  original_stored_pathname = stored_pathname; // if so, remember to create a symlink here
1538
                  if (pathbit_len > 0)
9 pmbaty 1539
                  {
34 pmbaty 1540
                     strncpy_s (candidate_pathname, MAXPATHLEN, stored_pathname, pathbit_len);
1541
                     strcpy_s (&candidate_pathname[pathbit_len], MAXPATHLEN - pathbit_len, canonical_dylib_name);
9 pmbaty 1542
                  }
34 pmbaty 1543
                  else
1544
                     strcpy_s (candidate_pathname, MAXPATHLEN, canonical_dylib_name);
1545
                  stored_pathname = candidate_pathname;
7 pmbaty 1546
               }
1 pmbaty 1547
            }
9 pmbaty 1548
         } // end if the file we're storing is a dylib
10 pmbaty 1549
 
1550
         // now strip this ELF file if necessary
1551
         if (!(entry_parms->extra_ino_flags & IFS_INO_PROCESSED_ELF))
1552
         {
20 pmbaty 1553
            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 1554
            entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF; // mark this inode as a preprocessed ELF file
1555
         } // end if the file is not yet a processed ELF
9 pmbaty 1556
      } // end if the file we're storing is an ELF file
16 pmbaty 1557
      #undef ELFHDR // undefine the macro that used to always point to the ELF header at the beginning of the file
1 pmbaty 1558
   }
1559
 
18 pmbaty 1560
   // have a pointer to where the stored pathname actually starts, without the leading slash
1561
   stored_pathname_without_leading_slash = stored_pathname[0] == '/' ? &stored_pathname[1] : stored_pathname;
1562
 
1563
   // see if this item already has an entry in the current list of filesystem entries
1564
   for (fsentry_index = 0; fsentry_index < *fsentry_count; fsentry_index++)
1565
   {
1566
      fsentry = &(*fsentries)[fsentry_index]; // quick access to fs entry slot
1567
      if (   (S_ISDIR  (fsentry->header.mode) && (strcmp (fsentry->u.dir.path,     stored_pathname_without_leading_slash) == 0))
19 pmbaty 1568
          || (S_ISREG  (fsentry->header.mode) && (strcmp (fsentry->u.file.path,    stored_pathname_without_leading_slash) == 0))
1569
          || (S_ISLNK  (fsentry->header.mode) && (strcmp (fsentry->u.symlink.path, stored_pathname_without_leading_slash) == 0))
1570
          || (S_ISFIFO (fsentry->header.mode) && (strcmp (fsentry->u.symlink.path, stored_pathname_without_leading_slash) == 0)))
18 pmbaty 1571
         break; // stop searching as soon as we find a duplicate
1572
   }
1573
 
1574
   // is there already an entry for this item ?
1575
   if (fsentry_index < *fsentry_count)
1576
   {
34 pmbaty 1577
      // if we should NOT ignore duplicates, bomb out (except for the root entry which is implicitly defined), else just return
1578
      if (strcmp (stored_pathname, "/") == 0)
1579
         return; // the root entry is implicitly defined: do not warn about it
1580
      else if (entry_parms->should_ignore_duplicates)
1581
         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);
1582
      else
1583
         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 1584
   }
1585
   else // this is a new entry: grow filesystem entries array to hold one more slot
1586
   {
1587
      reallocated_ptr = realloc (*fsentries, (*fsentry_count + 1) * sizeof (fsentry_t)); // attempt to reallocate
1588
      ASSERT_WITH_ERRNO (reallocated_ptr); // verify
1589
      *fsentries = reallocated_ptr; // save reallocated pointer
1590
      fsentry = &(*fsentries)[*fsentry_count]; // quick access to fs entry slot
1591
      (*fsentry_count)++; // remember there's one entry more in the array
1592
   }
1593
 
1594
   // save (or update) this entry's parameters
1 pmbaty 1595
   fsentry->header.extattr_offset = 0;
10 pmbaty 1596
   fsentry->header.ino = entry_parms->extra_ino_flags | (++inode_count);
1 pmbaty 1597
   fsentry->header.mode = entry_parms->st_mode;
1598
   fsentry->header.gid = entry_parms->gid;
1599
   fsentry->header.uid = entry_parms->uid;
7 pmbaty 1600
   fsentry->header.mtime = (entry_parms->mtime == UINT32_MAX ? (uint32_t) time (NULL) : entry_parms->mtime);
1 pmbaty 1601
   if (S_ISDIR (entry_parms->st_mode))
1602
   {
18 pmbaty 1603
      fsentry->u.dir.path = strdup (stored_pathname_without_leading_slash);
16 pmbaty 1604
 
7 pmbaty 1605
      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 1606
      fsentry->UNSAVED_was_data_written = true; // no data to save
1607
   }
1608
   else if (S_ISREG (entry_parms->st_mode))
1609
   {
1610
      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 1611
      fsentry->u.file.size = (uint32_t) entry_parms->data.size;
18 pmbaty 1612
      fsentry->u.file.path = strdup (stored_pathname_without_leading_slash);
16 pmbaty 1613
 
7 pmbaty 1614
      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 1615
      fsentry->UNSAVED_databuf = malloc (entry_parms->data.size);
1616
      ASSERT_WITH_ERRNO (fsentry->UNSAVED_databuf);
1617
      memcpy (fsentry->UNSAVED_databuf, entry_parms->data.bytes, entry_parms->data.size);
1 pmbaty 1618
      fsentry->UNSAVED_was_data_written = false; // there *IS* data to save
1619
   }
1620
   else if (S_ISLNK (entry_parms->st_mode))
1621
   {
18 pmbaty 1622
      fsentry->u.symlink.sym_offset = (uint16_t) (strlen (stored_pathname_without_leading_slash) + 1);
16 pmbaty 1623
      fsentry->u.symlink.sym_size = (uint16_t) entry_parms->data.size;
18 pmbaty 1624
      fsentry->u.symlink.path = strdup (stored_pathname_without_leading_slash);
38 pmbaty 1625
      fsentry->u.symlink.contents = strdup ((const char *) entry_parms->data.bytes);
16 pmbaty 1626
      ASSERT_WITH_ERRNO (fsentry->u.symlink.contents);
1627
 
7 pmbaty 1628
      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 1629
      fsentry->UNSAVED_was_data_written = true; // no data to save
1630
   }
7 pmbaty 1631
   else // necessarily a device node
1 pmbaty 1632
   {
38 pmbaty 1633
      fsentry->u.device.dev  = strtol ((const char *) entry_parms->data.bytes, NULL, 0); // use strtol() to parse decimal (...), hexadecimal (0x...) and octal (0...) numbers
1634
      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 1635
      fsentry->u.device.path = strdup (stored_pathname_without_leading_slash);
16 pmbaty 1636
 
7 pmbaty 1637
      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 1638
      fsentry->UNSAVED_was_data_written = true; // no data to save
1639
   }
7 pmbaty 1640
 
34 pmbaty 1641
   fsentry->UNSAVED_lineno = lineno; // save the line number at which this entry was defined, for error reporting
1642
 
7 pmbaty 1643
   // should we also add a symlink to this entry ? (in case we stored a dylib file under its canonical name)
1644
   if (original_stored_pathname != NULL)
1645
   {
34 pmbaty 1646
      old_data = entry_parms->data.bytes; // backup previous data pointer
7 pmbaty 1647
      entry_parms->is_compiled_bootscript = false;
1648
      entry_parms->should_autosymlink_dylib = false;
1649
      entry_parms->should_follow_symlinks = false;
1650
      entry_parms->st_mode = S_IFLNK | 0777; // NOTE: mkifs stores symlink permissions as rwxrwxrwx !
10 pmbaty 1651
      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 1652
      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 1653
      entry_parms->data.size = strlen ((const char *) entry_parms->data.bytes);
7 pmbaty 1654
      add_fsentry (fsentries, fsentry_count, entry_parms, original_stored_pathname, NULL);
11 pmbaty 1655
      entry_parms->data.bytes = old_data; // restore previous data pointer so that it can be freed normally
7 pmbaty 1656
   }
1657
 
34 pmbaty 1658
#if 0 // DISABLED: not the right place to do that (causes duplicates)
1659
   // 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.
1660
   if ((canonical_dylib_name != NULL) && ((token = strstr (stored_pathname, ".so.")) != NULL))
1661
   {
1662
      old_data = entry_parms->data.bytes; // backup previous data pointer
1663
      entry_parms->is_compiled_bootscript = false;
1664
      entry_parms->should_autosymlink_dylib = false;
1665
      entry_parms->should_follow_symlinks = false;
1666
      entry_parms->st_mode = S_IFLNK | 0777; // NOTE: mkifs stores symlink permissions as rwxrwxrwx !
1667
      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
1668
      entry_parms->data.bytes = (uint8_t *) ((last_dirsep = strrchr (stored_pathname, '/')) == NULL ? stored_pathname : last_dirsep + 1); // store symlink target in dirent data
1669
      entry_parms->data.size = strlen (entry_parms->data.bytes);
1670
      token[3] = 0;
1671
      add_fsentry (fsentries, fsentry_count, entry_parms, stored_pathname, NULL);
1672
      token[3] = '.';
1673
      entry_parms->data.bytes = old_data; // restore previous data pointer so that it can be freed normally
1674
   }
1675
#endif
1676
 
19 pmbaty 1677
   return; // finished, return to our caller
1 pmbaty 1678
}
1679
 
1680
 
19 pmbaty 1681
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)
1682
{
1683
   // adds the contents of the directory pointed to by dir_pathname to the fsentries array, recursively
1684
   // start_pathname_len is initialized to the length of dir_pathname by the top caller, and passed down unchanged,
1685
   // so that each sublevel of the recursion knows the depth of the relative path in which it is.
1686
 
1687
   thread_local static char item_pathname[MAXPATHLEN] = "";
1688
   thread_local static parms_t entry_parms = { 0 };
1689
   thread_local static struct stat stat_buf = { 0 };
1690
   thread_local static char major_minor[64];
1691
 
1692
   DIR *dirp;
1693
   struct dirent *dp;
1694
 
1695
   // open the directory
1696
   dirp = opendir (dir_pathname);
1697
   if (dirp == NULL)
1698
      DIE_WITH_EXITCODE (1, "unable to open directory \"%s\" for recursive inclusion", dir_pathname);
1699
 
1700
   // enumerate its contents
1701
   while ((dp = readdir (dirp)) != NULL)
1702
   {
1703
      if ((strcmp (dp->d_name, ".") == 0) || (strcmp (dp->d_name, "..") == 0))
1704
         continue; // skip self and parent
1705
 
1706
      memcpy (&entry_parms, default_parms, sizeof (parms_t));
1707
      sprintf_s (item_pathname, sizeof (item_pathname), "%s/%s", dir_pathname, dp->d_name); // construct item's pathname
1708
      ASSERT_WITH_ERRNO (stat (item_pathname, &stat_buf) == 0); // peek info about this entry (or die trying)
1709
      if (S_ISDIR (stat_buf.st_mode))
1710
      {
1711
         entry_parms.st_mode |= entry_parms.dperms; // apply DIRECTORY default permissions
1712
         add_fsentry (fsentries, fsentry_count, &entry_parms, &item_pathname[start_pathname_len], NULL); // add a filesystem entry of type "directory"
1713
         add_directory_contents_recursively (fsentries, fsentry_count, item_pathname, start_pathname_len, default_parms); // dwell into this directory and add its children recursively
1714
      }
1715
      else if (S_ISLNK (stat_buf.st_mode))
1716
      {
1717
         entry_parms.st_mode |= 0777; // NOTE: mkifs sets symlink permissions to rwxrwxrwx !?
1718
         add_fsentry (fsentries, fsentry_count, &entry_parms, &item_pathname[start_pathname_len], item_pathname); // add a filesystem entry of type "link"
1719
      }
1720
      else if (S_ISREG (stat_buf.st_mode))
1721
      {
1722
         entry_parms.st_mode |= entry_parms.perms; // apply FILE default permissions
1723
         add_fsentry (fsentries, fsentry_count, &entry_parms, &item_pathname[start_pathname_len], item_pathname); // add a filesystem entry of type "regular file"
1724
      }
1725
      else if (S_ISFIFO (stat_buf.st_mode))
1726
      {
1727
         entry_parms.st_mode |= entry_parms.perms; // apply FILE default permissions
1728
         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 1729
         entry_parms.data.bytes = (uint8_t *) major_minor;
19 pmbaty 1730
         add_fsentry (fsentries, fsentry_count, &entry_parms, &item_pathname[start_pathname_len], NULL); // add a filesystem entry of type "FIFO"
1731
      }
1732
      else
1733
         LOG_WARNING ("ignoring unsupported directory entry: \"%s\" (type 0%o)", item_pathname, stat_buf.st_mode & S_IFMT);
1734
   }
1735
 
1736
   closedir (dirp); // finished parsing this level, close the directory handle
1737
   return; // and return to our caller
1738
}
1739
 
1740
 
1 pmbaty 1741
static int fsentry_compare_pathnames_cb (const void *a, const void *b)
1742
{
1743
   // qsort() callback that compares two imagefs filesystem entries and sort them alphabetically by pathname
1744
 
3 pmbaty 1745
   const fsentry_t *entry_a = (const fsentry_t *) a;
1746
   const fsentry_t *entry_b = (const fsentry_t *) b;
1 pmbaty 1747
   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)));
1748
   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)));
1749
   return (strcmp (pathname_a, pathname_b));
1750
}
1751
 
1752
 
17 pmbaty 1753
static void parse_line (FILE *buildfile_fp, char *line_buffer, fsentry_t **fsentries, size_t *fsentry_count, parms_t *default_parms)
1 pmbaty 1754
{
34 pmbaty 1755
   thread_local static char specified_pathname[MAXPATHLEN] = ""; // exactly as specified in the build file
17 pmbaty 1756
   thread_local static char path_on_buildhost[MAXPATHLEN] = "";
1757
   thread_local static char path_in_ifs[MAXPATHLEN] = "";
1758
   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 1759
 
17 pmbaty 1760
   bool should_discard_inline_contents;
1761
   bool is_quoted_context;
1762
   bool is_escaped_char;
1763
   struct tm utc_time;
1764
   void *reallocated_ptr;
1765
   size_t allocated_size;
1766
   size_t string_len;
1767
   char *attrblock_start;
34 pmbaty 1768
   char *pathname_start;
17 pmbaty 1769
   char *write_ptr;
1770
   char *line_ptr;
1771
   char *value;
7 pmbaty 1772
   char *token;
17 pmbaty 1773
   char *sep;
1774
   char *ctx;
1775
   int read_char;
1776
 
1777
   line_ptr = line_buffer;
1778
   while ((*line_ptr != 0) && isspace (*line_ptr))
1779
      line_ptr++; // skip leading spaces
1780
 
1781
   if ((*line_ptr == 0) || (*line_ptr == '#'))
19 pmbaty 1782
      return; // don't process empty lines and comments
17 pmbaty 1783
 
1784
   string_len = (int) strlen (line_buffer);
34 pmbaty 1785
   while ((string_len > 0) && ((line_buffer[string_len - 1] == '\r') || (line_buffer[string_len - 1] == '\n')))
1786
      line_buffer[--string_len] = 0; // chop off carriage returns and newlines for easier debug output
17 pmbaty 1787
 
1788
   // reset entry values
1789
   memcpy (&entry_parms, default_parms, sizeof (parms_t));
1790
   path_in_ifs[0] = 0;
1791
   path_on_buildhost[0] = 0;
34 pmbaty 1792
   specified_pathname[0] = 0;
17 pmbaty 1793
   should_discard_inline_contents = false;
1794
 
1795
   // does this line start with an attribute block ?
1796
   if (*line_ptr == '[')
7 pmbaty 1797
   {
17 pmbaty 1798
      line_ptr++; // skip the leading square bracket
1799
      attrblock_start = line_ptr; // remember where it starts
1800
      is_quoted_context = false;
1801
      while ((*line_ptr != 0) && !((*line_ptr == ']') && (line_ptr[-1] != '\\') && !is_quoted_context))
1802
      {
1803
         if (*line_ptr == '"')
1804
            is_quoted_context ^= true; // remember when we're between quotes
1805
         else if (!is_quoted_context && (*line_ptr == ' '))
1806
            *line_ptr = RECORD_SEP[0]; // turn all spaces outside quoted contexts into an ASCII record separator to ease token splitting
1807
         line_ptr++; // reach the next unescaped closing square bracket
1808
      }
1809
      if (*line_ptr != ']')
1810
      {
1811
         LOG ("warning", 0, "syntax error in \"%s\" line %d: unterminated attributes block (skipping)", buildfile_pathname, lineno);
1812
         return; // invalid attribute block, skip line
1813
      }
1814
      *line_ptr = 0; // end the attribute block so that it is a parsable C string
7 pmbaty 1815
 
17 pmbaty 1816
      // now parse the attribute tokens
1817
      // DOCUMENTATION: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/m/mkifs.html#mkifs__description
1818
      token = strtok_r (attrblock_start, RECORD_SEP, &ctx);
1819
      while (token != NULL)
1820
      {
1821
         // evaluate attribute token
1822
         #define REACH_TOKEN_VALUE() do { value = strchr (token, '=') + 1; if (*value == '"') value++; } while (0)
1823
         if (false) {}
1824
         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 1825
         else if (strncmp (token, "uid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.uid     = (int) read_integer (value, 10); }
1826
         else if (strncmp (token, "gid=",     4) == 0) { REACH_TOKEN_VALUE (); entry_parms.gid     = (int) read_integer (value, 10); }
1827
         else if (strncmp (token, "dperms=",  7) == 0) { REACH_TOKEN_VALUE (); entry_parms.dperms  = (int) read_integer (value, 8); }
1828
         else if (strncmp (token, "perms=",   6) == 0) { REACH_TOKEN_VALUE (); entry_parms.perms   = (int) read_integer (value, 8); }
17 pmbaty 1829
         else if (strncmp (token, "type=",    5) == 0) { REACH_TOKEN_VALUE ();
1830
            if      (strcmp (value, "dir")  == 0) entry_parms.st_mode = S_IFDIR;
1831
            else if (strcmp (value, "file") == 0) entry_parms.st_mode = S_IFREG;
1832
            else if (strcmp (value, "link") == 0) entry_parms.st_mode = S_IFLNK;
1833
            else if (strcmp (value, "fifo") == 0) entry_parms.st_mode = S_IFIFO;
1834
            else DIE_WITH_EXITCODE (1, "invalid 'type' attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, value);
1835
         }
1836
         else if (strncmp (token, "image=",   6) == 0) { REACH_TOKEN_VALUE ();
34 pmbaty 1837
            image_base = (uint32_t) read_integer (value, 0); // read image base address
1838
            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.)
1839
            if ((sep = strchr (value, ',')) != NULL) image_maxsize   = (uint32_t) read_integer (sep + 1, 0); // if we have a comma, read optional image max size
1840
            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
1841
            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 1842
            LOG_INFO ("image 0x%x-0x%x maxsize %d totalsize %d align %d", image_base, image_end, image_maxsize, image_totalsize, image_align);
1843
         }
1844
         else if (strncmp (token, "virtual=", 8) == 0) { REACH_TOKEN_VALUE ();
1845
            if ((sep = strchr (value, ',')) != NULL) // do we have a comma separating (optional) processor and boot file name ?
1846
            {
1847
               *sep = 0;
30 pmbaty 1848
               if (strcmp (value, "x86_64") == 0)
1849
               {
1850
                  image_processor = "x86_64"; // save processor
1851
                  image_processor_base = "x86_64"; // save processor base
1852
                  image_pagesize = 4 * 1024; // Intel processors use 4 Kb pages
1853
               }
1854
               else if (strcmp (value, "aarch64le") == 0)
1855
               {
1856
                  image_processor = "aarch64le"; // save processor
1857
                  image_processor_base = "aarch64"; // save processor base
1858
                  image_pagesize = 16 * 1024; // ARM processors use 16 Kb pages
1859
               }
1860
               else
1861
                  DIE_WITH_EXITCODE (1, "unrecognized processor type in 'virtual' attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, value);
17 pmbaty 1862
               value = sep + 1;
1863
            }
34 pmbaty 1864
            boot_type = (strstr (value, "uefi") != NULL ? BOOTTYPE_UEFI : BOOTTYPE_BIOS); // FIXME: this should be a boot FILE, not a hardcoded tag
1865
            if (boot_type == BOOTTYPE_UEFI)
1866
               LOG_INFO ("processor \"%s\" bootfile <generated, UEFI>\n", image_processor);
1867
            else
41 pmbaty 1868
               LOG_INFO ("processor \"%s\" bootfile <boot.bios, BIOS>\n", image_processor);
20 pmbaty 1869
            entry_parms.is_bootstrap_file = true;
17 pmbaty 1870
         }
1871
         else if (strncmp (token, "mtime=", 6) == 0) { REACH_TOKEN_VALUE (); if (strcmp (value, "*") == 0) entry_parms.mtime = UINT32_MAX; else {
1872
               // value *must* be "YYYY-MM-DD-HH:MM:SS" by specification
1873
               memset (&utc_time, 0, sizeof (utc_time));
1874
               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)
1875
               {
1876
                  LOG_WARNING ("syntax error in \"%s\" line %d: mtime specification not in YYYY-MM-DD-HH:MM:SS format (skipping)", buildfile_pathname, lineno);
1877
                  continue; // invalid attribute block, skip line
1878
               }
1879
               utc_time.tm_mon--; // convert month from [1-12] to [0-11]
1880
               entry_parms.mtime = (uint32_t) mktime (&utc_time);
1881
            }
1882
         }
26 pmbaty 1883
         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)); }
1884
         else if (strcmp (token, "+compress")   == 0) startup_header_compression_flag = STARTUP_HDR_FLAGS1_COMPRESS_UCL;
1885
         else if (strcmp (token, "-compress")   == 0) startup_header_compression_flag = STARTUP_HDR_FLAGS1_COMPRESS_NONE;
22 pmbaty 1886
         else if (strcmp (token, "+script")     == 0) entry_parms.is_compiled_bootscript         = true;
19 pmbaty 1887
         else if (strcmp (token, "-script")     == 0) entry_parms.is_compiled_bootscript         = false;
1888
         else if (strcmp (token, "+followlink") == 0) entry_parms.should_follow_symlinks         = true;
1889
         else if (strcmp (token, "-followlink") == 0) entry_parms.should_follow_symlinks         = false;
1890
         else if (strcmp (token, "+autolink")   == 0) entry_parms.should_autosymlink_dylib       = true;
1891
         else if (strcmp (token, "-autolink")   == 0) entry_parms.should_autosymlink_dylib       = false;
1892
         else if (strcmp (token, "+keeplinked") == 0) entry_parms.should_keep_ld_output          = true;
1893
         else if (strcmp (token, "-keeplinked") == 0) entry_parms.should_keep_ld_output          = false;
1894
         else if (strcmp (token, "+dupignore")  == 0) entry_parms.should_ignore_duplicates       = true;
1895
         else if (strcmp (token, "-dupignore")  == 0) entry_parms.should_ignore_duplicates       = false;
18 pmbaty 1896
         else if (strcmp (token, "+optional")   == 0) entry_parms.should_allow_nonexistent_files = true;
1897
         else if (strcmp (token, "-optional")   == 0) entry_parms.should_allow_nonexistent_files = false;
17 pmbaty 1898
         else LOG_WARNING ("unimplemented attribute in \"%s\" line %d: '%s'", buildfile_pathname, lineno, token);
1899
         #undef REACH_TOKEN_VALUE
7 pmbaty 1900
 
17 pmbaty 1901
         token = strtok_r (NULL, RECORD_SEP, &ctx); // proceed to next attribute token
1902
      }
1903
 
1904
      line_ptr++; // reach the next character
1905
      while ((*line_ptr != 0) && isspace (*line_ptr))
1906
         line_ptr++; // skip leading spaces
1907
 
1908
      // are we at the end of the line ? if so, it means the attribute values that are set should become the default
1909
      if ((*line_ptr == 0) || (*line_ptr == '#'))
1910
      {
1911
         #define APPLY_DEFAULT_ATTR_NUM(attr,descr,fmt) do { if (entry_parms.attr != default_parms->attr) { \
1912
               LOG_INFO ("changing default " descr " from " fmt " to " fmt " by attribute at \"%s\" line %d", default_parms->attr, entry_parms.attr, buildfile_pathname, lineno); \
1913
               default_parms->attr = entry_parms.attr; \
1914
            } } while (0)
1915
         #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))) { \
1916
            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 1917
               if (default_parms->attr != NULL) free (default_parms->attr); \
1918
               default_parms->attr = strdup (entry_parms.attr); \
1919
               ASSERT_WITH_ERRNO (default_parms->attr != NULL); \
17 pmbaty 1920
            } } while (0)
18 pmbaty 1921
         //APPLY_DEFAULT_ATTR_STR (new_cwd,                        "current working directory",       "\"%s\"");
1922
         APPLY_DEFAULT_ATTR_STR (search,                         "search path list",                "\"%s\"");
1923
         APPLY_DEFAULT_ATTR_STR (prefix,                         "prefix",                          "\"%s\"");
1924
         APPLY_DEFAULT_ATTR_NUM (dperms,                         "directory permissions",           "0%o");
1925
         APPLY_DEFAULT_ATTR_NUM (perms,                          "file permissions",                "0%o");
1926
         APPLY_DEFAULT_ATTR_NUM (uid,                            "owner ID",                        "%d");
1927
         APPLY_DEFAULT_ATTR_NUM (gid,                            "group ID",                        "%d");
1928
         APPLY_DEFAULT_ATTR_NUM (st_mode,                        "inode type",                      "0%o");
1929
         APPLY_DEFAULT_ATTR_NUM (is_compiled_bootscript,         "compiled script state",           "%d");
1930
         APPLY_DEFAULT_ATTR_NUM (should_follow_symlinks,         "symlink resolution flag",         "%d");
1931
         APPLY_DEFAULT_ATTR_NUM (should_autosymlink_dylib,       "dylib canonical name symlinking", "%d");
1932
         APPLY_DEFAULT_ATTR_NUM (should_keep_ld_output,          "linker output preservation flag", "%d");
1933
         APPLY_DEFAULT_ATTR_NUM (should_ignore_duplicates,       "ignore duplicates flag",          "%d");
1934
         APPLY_DEFAULT_ATTR_NUM (should_allow_nonexistent_files, "ignore nonexistent files flag",   "%d");
17 pmbaty 1935
         #undef APPLY_DEFAULT_ATTR_STR
1936
         #undef APPLY_DEFAULT_ATTR_NUM
1937
         return; // end of line reached, proceed to the next line
1938
      }
1939
      // end of attributes parsing
1940
   } // end of "this line starts with an attributes block"
1941
 
1942
   // there's data in this line. We expect a filename in the IFS. Read it and unescape escaped characters
1943
   is_quoted_context = (*line_ptr == '"');
1944
   if (is_quoted_context)
1945
      line_ptr++; // skip a possible initial quote
34 pmbaty 1946
   write_ptr = specified_pathname;
24 pmbaty 1947
   while ((*line_ptr != 0) && ((!is_quoted_context && (*line_ptr != '=') && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr != '"'))))
17 pmbaty 1948
   {
1949
      if (*line_ptr == '\\')
1950
      {
1951
         line_ptr++;
1952
         *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
1953
      }
1954
      else
1955
         *write_ptr++ = *line_ptr;
1956
      line_ptr++;
1957
   }
1958
   *write_ptr = 0; // terminate the string
1959
   if (is_quoted_context && (*line_ptr == '"'))
1960
      line_ptr++; // skip a possible final quote
7 pmbaty 1961
 
17 pmbaty 1962
   // we reached a space OR an equal sign
1963
   while ((*line_ptr != 0) && isspace (*line_ptr))
1964
      line_ptr++; // skip optional spaces after the filename in the IFS
1965
 
1966
   // do we have an equal sign ?
34 pmbaty 1967
   if (*line_ptr == '=')
17 pmbaty 1968
   {
1969
      line_ptr++; // skip the equal sign
1970
      while ((*line_ptr != 0) && isspace (*line_ptr))
1971
         line_ptr++; // skip optional spaces after the equal sign
1972
 
1973
      if (*line_ptr == 0)
1974
      {
1975
         LOG_WARNING ("syntax error in \"%s\" line %d: missing data specification after equal sign (skipping)", buildfile_pathname, lineno);
1976
         return; // invalid symlink specification, skip line
1977
      }
1978
 
34 pmbaty 1979
      // it may be either a path or a contents definition. Is it a content definition ?
17 pmbaty 1980
      if (*line_ptr == '{')
1981
      {
1982
         allocated_size = 0;
1983
 
1984
         line_ptr++; // skip the leading content definition
1985
         is_escaped_char = false;
1986
         for (;;)
1987
         {
1988
            read_char = fgetc (buildfile_fp);
1989
            if (read_char == EOF)
1990
               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 1991
            else if (read_char == '\r')
1992
               continue; // just ignore carriage returns (Microsoft end of line format)
17 pmbaty 1993
            else if ((read_char == '\\') && !is_escaped_char)
1994
               is_escaped_char = true; // remember the next char is escaped
1995
            else if ((read_char == '}') && !is_escaped_char)
1996
               break; // found an unescaped closing bracked, stop parsing
1997
            else
1998
            {
1999
               is_escaped_char = false; // any other char, meaning the next one will not be escaped
2000
               if (!should_discard_inline_contents) // only store the contents if we do NOT know the data yet
2001
               {
30 pmbaty 2002
                  if (entry_parms.data.size == allocated_size) // reallocate in 16k blocks
17 pmbaty 2003
                  {
30 pmbaty 2004
                     reallocated_ptr = realloc (entry_parms.data.bytes, allocated_size + 16384);
17 pmbaty 2005
                     ASSERT_WITH_ERRNO (reallocated_ptr);
2006
                     entry_parms.data.bytes = reallocated_ptr;
30 pmbaty 2007
                     allocated_size += 16384;
17 pmbaty 2008
                  }
2009
                  entry_parms.data.bytes[entry_parms.data.size++] = read_char;
2010
               }
2011
               if (read_char == '\n')
2012
                  lineno++; // update line counter as we parse the inline content
2013
            }
2014
         } // end for
2015
      }
2016
      else // not a content definition between { brackets }, must be either a pathname on the build host, or the target of a symlink
2017
      {
2018
         is_quoted_context = (*line_ptr == '"');
2019
         if (is_quoted_context)
2020
            line_ptr++; // skip a possible initial quote
34 pmbaty 2021
         pathname_start = line_ptr; // remember where the specified pathname starts
17 pmbaty 2022
         write_ptr = line_ptr; // now unescape all characters
24 pmbaty 2023
         while ((*line_ptr != 0) && ((!is_quoted_context && !isspace (*line_ptr)) || (is_quoted_context && (*line_ptr != '"'))))
17 pmbaty 2024
         {
2025
            if (*line_ptr == '\\')
2026
            {
2027
               line_ptr++;
2028
               *write_ptr++ = *line_ptr; // unescape characters that are escaped with '\'
2029
            }
2030
            else
2031
               *write_ptr++ = *line_ptr;
2032
            line_ptr++;
2033
         }
2034
         *write_ptr = 0; // terminate the string
2035
         if (is_quoted_context && (*line_ptr == '"'))
2036
            line_ptr++; // skip a possible final quote
2037
 
2038
         if (S_ISLNK (entry_parms.st_mode)) // are we storing a symlink ?
34 pmbaty 2039
            ASSERT_WITH_ERRNO (Buffer_InitWithCString (&entry_parms.data, pathname_start)); // if so, store the symlink target as the dirent's blob data
17 pmbaty 2040
         else // it's a build host filesystem path
34 pmbaty 2041
            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 2042
      }
2043
   }
2044
   else // no equal sign, meaning the file will have the same name on the build host filesystem
2045
   {
2046
      // consistency check: symlinks MUST have an equal sign
2047
      if (entry_parms.st_mode == S_IFLNK)
2048
      {
2049
         LOG_WARNING ("syntax error in \"%s\" line %d: missing equal sign and symlink target (skipping)", buildfile_pathname, lineno);
2050
         return; // invalid symlink specification, skip line
2051
      }
2052
 
34 pmbaty 2053
      // UNLESS we know we are storing (=creating empty) a directory, the path on the build host is the one specified
2054
      if (S_ISDIR (entry_parms.st_mode))
2055
         path_on_buildhost[0] = 0; // we're storing a new empty directory: path on build host is nil
2056
      else
2057
         strcpy_s (path_on_buildhost, sizeof (path_on_buildhost), specified_pathname); // the path on the build host is the one specified
2058
 
2059
      if ((specified_pathname[0] != '/') && ((token = strrchr (specified_pathname, '/')) != NULL))
2060
         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 2061
   }
2062
 
34 pmbaty 2063
   // was the specified path in the build file absolute or relative ?
2064
   if (specified_pathname[0] == '/')
2065
      strcpy_s (path_in_ifs, sizeof (path_in_ifs), specified_pathname); // path is absolute, use it verbatim
2066
   else // path is relative
2067
   {
2068
      string_len = sprintf_s (path_in_ifs, sizeof (path_in_ifs), "%s", (entry_parms.prefix != NULL ? entry_parms.prefix : "")); // use path prefix
2069
      while ((string_len > 0) && (path_in_ifs[string_len - 1] == '/'))
2070
         string_len--; // chop off any trailing slashes from prefix
2071
      path_in_ifs[string_len++] = '/'; // add ONE trailing slash
2072
      strcpy_s (&path_in_ifs[string_len], sizeof (path_in_ifs) - string_len, specified_pathname); // construct an absolute path with the IFS prefix
2073
   }
2074
 
17 pmbaty 2075
   // now add this entry to the image filesystem
2076
   if (S_ISDIR (entry_parms.st_mode))
34 pmbaty 2077
      entry_parms.st_mode |= entry_parms.dperms; // directory
17 pmbaty 2078
   else if (S_ISLNK (entry_parms.st_mode))
34 pmbaty 2079
      entry_parms.st_mode |= 0777; // symlink (NOTE: mkifs sets symlink permissions to rwxrwxrwx !?)
2080
   else if (S_ISREG (entry_parms.st_mode))
2081
      entry_parms.st_mode |= entry_parms.perms; // file
2082
   else
2083
      entry_parms.st_mode |= entry_parms.perms; // device node
17 pmbaty 2084
 
2085
   add_fsentry (fsentries, fsentry_count, &entry_parms, path_in_ifs, path_on_buildhost); // and add filesystem entry
2086
 
2087
   if (entry_parms.data.bytes != NULL)
2088
      free (entry_parms.data.bytes); // if blob data was allocated, free it
2089
 
2090
   return; //  finished parsing that line
1 pmbaty 2091
}
2092
 
2093
 
2094
int main (int argc, char **argv)
2095
{
2096
   // program entrypoint
2097
 
16 pmbaty 2098
   typedef struct ifs_offsets_s
2099
   {
2100
      size_t startupheader;
2101
      size_t startuptrailer;
2102
      size_t imageheader;
2103
      size_t imagedir;
2104
      size_t imagetrailer;
2105
   } ifs_offsets_t;
2106
   typedef struct ifs_s
2107
   {
2108
      buffer_t data;
2109
      ifs_offsets_t offsets;
2110
      size_t final_size; // final size: not known (because not set) until everything has been written
2111
   } ifs_t;
1 pmbaty 2112
 
19 pmbaty 2113
   startup_header_t startup_header = { 0 }; // output IFS's startup header
2114
   startup_trailer_v2_t startup_trailer = { 0 }; // output IFS's startup trailer (version 2, with SHA-512 checksum and int32 checksum)
2115
   image_header_t image_header = { 0 }; // output IFS's imagefs header
2116
   image_trailer_v2_t image_trailer = { 0 }; // output IFS's imagefs trailer (version 2, with SHA-512 checksum and int32 checksum)
2117
   fsentry_t *fsentries = NULL; // output IFS's filesystem entries
2118
   size_t fsentry_count = 0; // number of entries in the IFS filesystem
2119
   parms_t default_parms = { // default parameters for a filesystem entry
7 pmbaty 2120
      .dperms = 0755,
2121
      .perms = 0644,
2122
      .mtime = UINT32_MAX,
2123
      .mtime_for_inline_files = UINT32_MAX,
24 pmbaty 2124
      .prefix = NULL, // will be initialized to a *mallocated* string: "/proc/boot"
7 pmbaty 2125
      .should_follow_symlinks = true, // [+|-followlink]
2126
      .should_autosymlink_dylib = true, // [+|-autolink]
2127
   };
19 pmbaty 2128
   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 2129
 
41 pmbaty 2130
   char textbase_mapaddr_string[64];
2131
   elf_program_header_t *phdr = NULL;
34 pmbaty 2132
   uefi64_header_t *uefi_header = NULL;
7 pmbaty 2133
   char path_on_buildhost[MAXPATHLEN] = "";
2134
   char path_in_ifs[MAXPATHLEN] = "";
19 pmbaty 2135
   const char *ifs_pathname = NULL;
2136
   const char *rootdir_pathname = NULL;
2137
   const fsentry_t *fsentry;
1 pmbaty 2138
   void *reallocated_ptr;
26 pmbaty 2139
   buffer_t compressed_imagefs;
2140
   uint8_t *compressor_out;
2141
   uint8_t *compressor_in;
41 pmbaty 2142
   uint8_t *bootcode_start;
2143
   size_t program_header_table_count;
2144
   size_t cumulated_segment_length;
2145
   size_t minimal_padded_length;
2146
   size_t first_segment_offset;
26 pmbaty 2147
   size_t compressor_outlen;
2148
   size_t compressor_inlen;
17 pmbaty 2149
   size_t reallocated_size;
7 pmbaty 2150
   size_t available_space;
1 pmbaty 2151
   size_t fsentry_index;
7 pmbaty 2152
   size_t largest_index;
2153
   size_t largest_size;
17 pmbaty 2154
   size_t imgdir_size;
1 pmbaty 2155
   size_t curr_offset;
41 pmbaty 2156
   size_t table_index;
26 pmbaty 2157
   size_t remaining_len;
16 pmbaty 2158
   ifs_t ifs = { 0 };
8 pmbaty 2159
   int32_t checksum;
41 pmbaty 2160
   char *resolved_pathname;
16 pmbaty 2161
   char *first_pathname = NULL;
2162
   char *second_pathname = NULL;
19 pmbaty 2163
   char *third_pathname = NULL;
1 pmbaty 2164
   int arg_index;
2165
   bool is_quoted_context = false;
2166
   bool is_escaped_char = false;
11 pmbaty 2167
   bool should_discard_inline_contents = false;
1 pmbaty 2168
   bool want_info = false;
10 pmbaty 2169
   bool want_everything = false;
1 pmbaty 2170
   bool want_help = false;
14 pmbaty 2171
   bool want_dump = false;
16 pmbaty 2172
   bool want_strip = false;
15 pmbaty 2173
   bool want_hexdump = false;
26 pmbaty 2174
   bool hide_filename = false;
5 pmbaty 2175
   bool is_foreign_endianness;
26 pmbaty 2176
   int compressor_ret;
1 pmbaty 2177
   FILE *buildfile_fp;
2178
 
17 pmbaty 2179
   // initialize stuff
2180
   saved_ELF_sections = (char **) malloc (4 * sizeof (char *));
2181
   ASSERT_WITH_ERRNO (saved_ELF_sections);
2182
   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
2183
   saved_ELF_sections[1] = ".gnu_debuglink";
2184
   saved_ELF_sections[2] = "QNX_usage";
2185
   saved_ELF_sections[3] = ".note.gnu.build-id"; // undocumented by QNX, but nonetheless preserved
2186
   saved_ELF_section_count = 4;
24 pmbaty 2187
   default_parms.prefix = strdup ("/proc/boot");
2188
   ASSERT_WITH_ERRNO (default_parms.prefix);
17 pmbaty 2189
 
1 pmbaty 2190
   // parse arguments
2191
   for (arg_index = 1; arg_index < argc; arg_index++)
2192
   {
41 pmbaty 2193
      if ((strcmp (argv[arg_index], "-a") == 0) && (arg_index + 1 < argc)) // -a suffix
19 pmbaty 2194
         sym_suffix = argv[++arg_index];
7 pmbaty 2195
      else if (strcmp (argv[arg_index], "-n") == 0)
2196
         default_parms.mtime_for_inline_files = 0; // inline files should have a mtime set to zero
2197
      else if (strcmp (argv[arg_index], "-nn") == 0)
2198
      {
11 pmbaty 2199
         default_parms.mtime = 0; // *all* files should have a mtime set to zero
7 pmbaty 2200
         default_parms.mtime_for_inline_files = 0;
2201
      }
14 pmbaty 2202
      else if ((strcmp (argv[arg_index], "--outdir") == 0) && (arg_index + 1 < argc)) // --outdir path
16 pmbaty 2203
         second_pathname = argv[++arg_index];
2204
      else if ((strcmp (argv[arg_index], "--outfile") == 0) && (arg_index + 1 < argc)) // --outfile pathname
2205
         second_pathname = argv[++arg_index];
1 pmbaty 2206
      else if (strcmp (argv[arg_index], "--info") == 0)
2207
         want_info = true;
14 pmbaty 2208
      else if (strcmp (argv[arg_index], "--dump") == 0)
2209
         want_dump = true;
15 pmbaty 2210
      else if (strcmp (argv[arg_index], "--hexdump") == 0) // voluntarily undocumented
2211
         want_hexdump = true;
16 pmbaty 2212
      else if (strcmp (argv[arg_index], "--strip") == 0)
2213
         want_strip = true;
10 pmbaty 2214
      else if (strcmp (argv[arg_index], "--everything") == 0)
2215
         want_everything = true;
26 pmbaty 2216
      else if (strcmp (argv[arg_index], "--hide-filename") == 0)
2217
         hide_filename = true;
15 pmbaty 2218
      else if (strncmp (argv[arg_index], "-v", 2) == 0) // -v[....]
2219
         verbose_level += (int) strlen (argv[arg_index] + 1); // increase verbosity by the number of characters in this flag
17 pmbaty 2220
      else if ((strcmp (argv[arg_index], "-l") == 0) && (arg_index + 1 < argc))
2221
         arg_index++; // these args will be parsed once the build file is open
2222
      else if ((strcmp (argv[arg_index], "-r") == 0) && (arg_index + 1 < argc))
2223
      {
2224
         reallocated_size = (SEARCH_PATH != NULL ? strlen (SEARCH_PATH) + 1 : 0) + strlen (argv[arg_index + 1]) + 1;
2225
         reallocated_ptr = realloc (SEARCH_PATH, reallocated_size); // grow search prefixes array
2226
         ASSERT_WITH_ERRNO (reallocated_ptr);
2227
         if (SEARCH_PATH != NULL)
2228
            strcat_s (reallocated_ptr, reallocated_size, PATH_SEP);
2229
         strcat_s (reallocated_ptr, reallocated_size, argv[++arg_index]); // stack up another search prefix
2230
         SEARCH_PATH = reallocated_ptr;
2231
      }
2232
      else if ((strcmp (argv[arg_index], "-s") == 0) && (arg_index + 1 < argc))
2233
      {
2234
         reallocated_ptr = realloc (saved_ELF_sections, (saved_ELF_section_count + 1) * sizeof (char *)); // grow ELF sections array
2235
         ASSERT_WITH_ERRNO (reallocated_ptr);
2236
         saved_ELF_sections = reallocated_ptr;
2237
         saved_ELF_sections[saved_ELF_section_count++] = argv[++arg_index]; // stack up another ELF section name to preserve
2238
      }
1 pmbaty 2239
      else if ((strcmp (argv[arg_index], "-?") == 0) || (strcmp (argv[arg_index], "--help") == 0))
2240
         want_help = true;
20 pmbaty 2241
      else if ((first_pathname == NULL) && (*argv[arg_index] != '-'))
16 pmbaty 2242
         first_pathname = argv[arg_index];
20 pmbaty 2243
      else if ((second_pathname == NULL) && (*argv[arg_index] != '-'))
16 pmbaty 2244
         second_pathname = argv[arg_index];
20 pmbaty 2245
      else if ((third_pathname == NULL) && (*argv[arg_index] != '-'))
19 pmbaty 2246
         third_pathname = argv[arg_index];
16 pmbaty 2247
      else
2248
         DIE_WITH_EXITCODE (1, "unrecognized option: '%s'", argv[arg_index]);
1 pmbaty 2249
   }
2250
 
19 pmbaty 2251
   // do we want to display help ? (TODO: everything that's commented out is pending implementation)
2252
   if (want_help)
1 pmbaty 2253
   {
16 pmbaty 2254
      FILE *out = (want_help ? stdout : stderr); // select the right output channel
2255
      fprintf (out, "ifstool - QNX in-kernel filesystem creation utility by Pierre-Marie Baty <pm@pmbaty.com>\n");
2256
      fprintf (out, "          version " VERSION_FMT_YYYYMMDD "\n", VERSION_ARG_YYYYMMDD);
4 pmbaty 2257
      if (!want_help)
16 pmbaty 2258
         fprintf (out, "error: missing parameters\n");
2259
      fprintf (out, "usage:\n");
26 pmbaty 2260
      fprintf (out, "    ifstool --info [--everything] [--hide-filename] <ifs file>\n");
16 pmbaty 2261
      fprintf (out, "    ifstool --dump [--outdir <path>] <ifs file>\n");
2262
      fprintf (out, "    ifstool --strip [--outfile <pathname>] <ELF file>\n");
17 pmbaty 2263
      fprintf (out, "    ifstool [-?|--help]\n");
19 pmbaty 2264
      // mkifs [-?] [-l inputline] [-n[n]] [-o directory] [-p patchfile] [-r rootdir] [-s section] [-v] [buildfile] [directory] [outputfile]
41 pmbaty 2265
      fprintf (out, "    ifstool [-a suffix] [-l inputline] [-n[n]] [-r rootdir] [-s section] [-v[...]] [buildfile] [directory] [outputfile]\n");
21 pmbaty 2266
      fprintf (out, "NOTE: the compiler mode requires predigested boot and startup files produced by mkifs.\n");
17 pmbaty 2267
      fprintf (out, "options:\n");
2268
      fprintf (out, "    -?       Display some help information.\n");
19 pmbaty 2269
      fprintf (out, "    -a .ext  Append a suffix to symbol files generated via [+keeplinked].\n");
17 pmbaty 2270
      fprintf (out, "    -l line  Process line before interpreting the buildfile. Input lines given\n");
2271
      fprintf (out, "             to mkifs should be quoted to prevent interpretation by the shell\n");
2272
      fprintf (out, "             (especially as mkifs input lines often contain spaces). Multiple\n");
2273
      fprintf (out, "             -l options are processed in the order specified. No default.\n");
2274
      fprintf (out, "    -n[n]    Force the modification times of all inline files to be 0. If you\n");
2275
      fprintf (out, "             specify -nn, mkifs sets the modification times of all files to 0.\n");
2276
      fprintf (out, "             When mkifs adds files to an IFS image, it uses the timestamp info\n");
2277
      fprintf (out, "             from the file on the host machine. If mkifs is creating an inline\n");
2278
      fprintf (out, "             file (which doesn't exist on the host machine), it must generate\n");
2279
      fprintf (out, "             its own timestamp information. By default, it's the time at which\n");
2280
      fprintf (out, "             the image is generated. This results in different checksum values\n");
2281
      fprintf (out, "             for two identical builds, because the file's times are different.\n");
2282
      fprintf (out, "             If you use -n, the checksum value is the same on all identical\n");
2283
      fprintf (out, "             builds. The -nn option addresses a quirk in NTFS with daylight\n");
2284
      fprintf (out, "             savings time. This forces the modification time for all files in\n");
2285
      fprintf (out, "             the IFS image to be set to 0. This ensures that subsequent builds\n");
2286
      fprintf (out, "             of the same IFS image have the same checksum.");
2287
//      fprintf (out, "    -o dir   Specify a directory to be used for all permanent build artifacts,\n");
2288
//      fprintf (out, "             other than the output image itself. The most common example is\n");
2289
//      fprintf (out, "             the .sym files generated by the [+keeplinked] attribute.\n");
2290
//      fprintf (out, "    -p file  Apply patching instructions from this file.\n");
2291
      fprintf (out, "    -r dir   When searching for host files to be included in the image, search\n");
2292
      fprintf (out, "             the default paths used for storing binaries within the specified\n");
2293
      fprintf (out, "             directory before searching the default paths within $QNX_TARGET.\n");
2294
      fprintf (out, "             You can define multiple -r options; each adds a set of paths to\n");
2295
      fprintf (out, "             search for files. The -r options are evaluated from left to right\n");
2296
      fprintf (out, "             meaning the paths prefixed with the first (leftmost) rootdir are\n");
2297
      fprintf (out, "             searched first, then those prefixed with the second rootdir, and\n");
2298
      fprintf (out, "             so on.\n");
2299
      fprintf (out, "             Normally, mkifs searches any paths defined in $MKIFS_PATH when\n");
2300
      fprintf (out, "             it was called and then the default paths within $QNX_TARGET. The\n");
2301
      fprintf (out, "             default paths are based on the CPU architecture specified by\n");
2302
      fprintf (out, "             $PROCESSOR and $PROCESSOR_BASE. If you specify -r options, mkifs\n");
2303
      fprintf (out, "             searches the default paths prefixed with each dir variable before\n");
2304
      fprintf (out, "             searching those within $QNX_TARGET. These paths are:\n");
2305
      fprintf (out, "               dir/${PROCESSOR}/sbin\n");
2306
      fprintf (out, "               dir/${PROCESSOR}/usr/sbin\n");
2307
      fprintf (out, "               dir/${PROCESSOR}/boot/sys\n");
2308
      fprintf (out, "               dir/${PROCESSOR_BASE}/boot/sys\n");
2309
      fprintf (out, "               dir/${PROCESSOR}/bin\n");
2310
      fprintf (out, "               dir/${PROCESSOR}/usr/bin\n");
2311
      fprintf (out, "               dir/${PROCESSOR}/lib\n");
2312
      fprintf (out, "               dir/${PROCESSOR}/lib/dll\n");
2313
      fprintf (out, "               dir/${PROCESSOR}/usr/lib\n");
2314
      fprintf (out, "             NOTE: The structure of the directory paths under dir must be\n");
2315
      fprintf (out, "             identical to that of the default paths under $QNX_TARGET, but the\n");
2316
      fprintf (out, "             root dir itself may be any path you choose. For example, if you\n");
2317
      fprintf (out, "             wanted to include /scratch/aarch64le/sbin/devb-sata, you would\n");
2318
      fprintf (out, "             specify a -r option like this:\n");
2319
      fprintf (out, "               -r /scratch\n");
2320
      fprintf (out, "             Note that you don't include $PROCESSOR or $PROCESSOR_BASE in dir.\n");
2321
      fprintf (out, "    -s name  Don't strip the named section from ELF executables when creating\n");
2322
      fprintf (out, "             an IFS image. You can use this option more than once to specify\n");
2323
      fprintf (out, "             additional sections. By default, mkifs doesn't strip:\n");
2324
      fprintf (out, "               .gnu_debuglink - the name and checksum of the debug info file\n");
2325
      fprintf (out, "               QNX_info       - build properties\n");
2326
      fprintf (out, "               QNX_usage      - usage message\n");
2327
      fprintf (out, "             You can use the keepsection attribute to specify the sections\n");
2328
      fprintf (out, "             that are not to be stripped from specific files in the image. For\n");
2329
      fprintf (out, "             files in the bootstrap section (like startup or procnto), the\n");
2330
      fprintf (out, "             global keepsection list affected by -s does not apply to these\n");
2331
      fprintf (out, "             files. For them, only the QNX_info section is kept.\n");
2332
      fprintf (out, "    -v[v..]  Operate verbosely. Specifying additional v options increases the\n");
2333
      fprintf (out, "             verbosity.\n");
1 pmbaty 2334
      exit (want_help ? 0 : 1);
2335
   }
2336
 
19 pmbaty 2337
   // else do we want info about a particular IFS ? if so, dissecate it
2338
   else if (want_info)
26 pmbaty 2339
      exit (dump_ifs_info (first_pathname, want_everything, hide_filename));
2 pmbaty 2340
 
14 pmbaty 2341
   // else do we want to dump its contents ? if so, do so
2342
   else if (want_dump)
16 pmbaty 2343
      exit (dump_ifs_contents (first_pathname, (second_pathname != NULL ? second_pathname : ".")));
14 pmbaty 2344
 
15 pmbaty 2345
   // else do we want to hex dump a file ? (this is voluntarily undocumented)
2346
   else if (want_hexdump)
16 pmbaty 2347
      exit (dump_file_hex (first_pathname));
2348
 
2349
   // else do we want to strip an ELF file ? if so, do so
2350
   else if (want_strip)
15 pmbaty 2351
   {
16 pmbaty 2352
      buffer_t file;
2353
      ASSERT (Buffer_ReadFromFile (&file, first_pathname), "can't open \"%s\" for reading: %s", first_pathname, strerror (errno));
20 pmbaty 2354
      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 2355
      ASSERT_WITH_ERRNO (Buffer_WriteToFile (&file, (second_pathname != NULL ? second_pathname : "<stdout>")));
15 pmbaty 2356
      exit (0);
2357
   }
2358
 
16 pmbaty 2359
   // we want to CREATE an IFS file
2360
   buildfile_pathname = first_pathname; // assign the pathnames properly
19 pmbaty 2361
   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
2362
   rootdir_pathname = (third_pathname != NULL ? second_pathname : NULL);
16 pmbaty 2363
 
1 pmbaty 2364
   // make sure we have ${QNX_TARGET} pointing somewhere
2365
   QNX_TARGET = getenv ("QNX_TARGET");
2366
   if (QNX_TARGET == NULL)
15 pmbaty 2367
      DIE_WITH_EXITCODE (1, "the QNX_TARGET environment variable is not set");
1 pmbaty 2368
   else if (access (QNX_TARGET, 0) != 0)
15 pmbaty 2369
      DIE_WITH_EXITCODE (1, "the QNX_TARGET environment variable doesn't point to an existing directory");
1 pmbaty 2370
 
2371
   // open build file
19 pmbaty 2372
   if ((buildfile_pathname != NULL) && (strcmp (buildfile_pathname, "-") != 0))
2373
   {
2374
      fopen_s (&buildfile_fp, buildfile_pathname, "rb"); // open it
2375
      if (buildfile_fp == NULL)
2376
         DIE_WITH_EXITCODE (1, "unable to open build file \"%s\" for reading: %s", buildfile_pathname, strerror (errno));
2377
   }
2378
   else // no build file specified: use stdin
2379
   {
2380
      buildfile_pathname = "<stdin>";
2381
      buildfile_fp = stdin;
2382
   }
1 pmbaty 2383
 
2384
   // stack up filesystem entries
2385
   memcpy (&entry_parms, &default_parms, sizeof (default_parms));
2386
   entry_parms.st_mode = S_IFDIR | default_parms.dperms;
7 pmbaty 2387
   add_fsentry (&fsentries, &fsentry_count, &entry_parms, "", NULL); // add the root dir first
1 pmbaty 2388
 
17 pmbaty 2389
   // parse -l arguments before everything else
2390
   for (arg_index = 1; arg_index < argc; arg_index++)
2391
      if ((strcmp (argv[arg_index], "-l") == 0) && (arg_index + 1 < argc))
2392
         parse_line (NULL, argv[++arg_index], &fsentries, &fsentry_count, &default_parms);
2393
 
11 pmbaty 2394
   // parse the IFS build file line per line
1 pmbaty 2395
   while (fgets (line_buffer, sizeof (line_buffer), buildfile_fp) != NULL)
2396
   {
7 pmbaty 2397
      if (current_line != NULL)
2398
         free (current_line);
2399
      current_line = strdup (line_buffer);
16 pmbaty 2400
      ASSERT_WITH_ERRNO (current_line);
1 pmbaty 2401
      lineno++; // keep track of current line number
17 pmbaty 2402
      parse_line (buildfile_fp, line_buffer, &fsentries, &fsentry_count, &default_parms);
2403
   }
1 pmbaty 2404
 
17 pmbaty 2405
   fclose (buildfile_fp); // finished parsing the build file
1 pmbaty 2406
 
19 pmbaty 2407
   // if a root dir was specified, open it as a directory and recursively add all of its contents to the filesystem
2408
   if (rootdir_pathname != NULL)
2409
      add_directory_contents_recursively (&fsentries, &fsentry_count, rootdir_pathname, strlen (rootdir_pathname), &default_parms);
1 pmbaty 2410
 
16 pmbaty 2411
   //////////////////////////////////
2412
   // start constructing the IFS file
2413
 
2414
   Buffer_Initialize (&ifs.data);
2415
 
2 pmbaty 2416
   // do we have a startup file ? if so, this is a bootable image
41 pmbaty 2417
   if (startup_argv.count > 0)
1 pmbaty 2418
   {
2419
      // write boot prefix
34 pmbaty 2420
      if (boot_type == BOOTTYPE_UEFI) // UEFI boot
2421
      {
41 pmbaty 2422
         boot_code.size = ROUND_TO_UPPER_MULTIPLE (sizeof (uefi64_header_t), 512); // round to upper filesystem block (PE header constraint)
2423
 
34 pmbaty 2424
         ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, sizeof (uefi64_header_t))); // start by writing an empty UEFI header
2425
         uefi_header = (uefi64_header_t *) ifs.data.bytes; // have a convenience pointer
2426
         memcpy (&uefi_header->dos_header.signature, "MZ", 2); // store the MZ magic
2427
         uefi_header->dos_header.bytes_in_last_page = 144; // fixed value
2428
         uefi_header->dos_header.number_of_pages = 3; // fixed value
2429
         uefi_header->dos_header.header_size_in_paragraphs = sizeof (uefi_header->dos_header) / 16;
2430
         uefi_header->dos_header.requested_paragraphs = 0xffff;
2431
         uefi_header->dos_header.initial_stack_pointer_value = 0x00b8; // fixed value
2432
         uefi_header->dos_header.absolute_offset_to_relocation_table = sizeof (uefi_header->dos_header);
2433
         uefi_header->dos_header.absolute_offset_to_pe_header = sizeof (uefi_header->dos_header) + sizeof (uefi_header->dos_stub_bytes);
2434
         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
2435
         memcpy (&uefi_header->pe_header.signature, "PE\0\0", 4); // store the PE magic
2436
         uefi_header->pe_header.machine_type = (strcmp (image_processor, "x86_64") == 0 ? 0x8664 : (strcmp (image_processor, "aarch64le") == 0 ? 0xaa64 : 0xffff)); // store machine type
2437
         uefi_header->pe_header.number_of_sections = 1; // store number of sections
2438
         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)
2439
         uefi_header->pe_header.size_of_optional_header = sizeof (uefi_header->optional_header64); // 240 bytes
2440
         uefi_header->pe_header.characteristics_bitmap = 0x0223; // store characteristics bitmap (executable, uses large addresses, relocs stripped, debug info stripped)
2441
         memcpy (uefi_header->optional_header64.signature, "\x0b\x02", 2); // store the 64-bit optional header magic
2442
         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 2443
         uefi_header->optional_header64.entrypoint_address = WILL_BE_FILLED_LATER;
34 pmbaty 2444
         uefi_header->optional_header64.image_base = image_base;
2445
         uefi_header->optional_header64.section_alignment = (uint32_t) image_pagesize;
2446
         uefi_header->optional_header64.file_alignment = 512; // i.e. one filesystem block
2447
         uefi_header->optional_header64.image_size = WILL_BE_FILLED_LATER; // total IFS file size
41 pmbaty 2448
         uefi_header->optional_header64.size_of_headers = (uint32_t) boot_code.size;
34 pmbaty 2449
         uefi_header->optional_header64.subsystem_type = 10; // IMAGE_SUBSYSTEM_EFI_APPLICATION
2450
         uefi_header->optional_header64.stack_reserve_size = image_pagesize;
2451
         uefi_header->optional_header64.stack_commit_size = image_pagesize;
2452
         uefi_header->optional_header64.number_of_data_directories = 16; // mkifs reserves 16 data directories, filled with zeroes (FIXME: why?)
2453
         memcpy (uefi_header->unique_section.section_name, "image\0\0\0", 8); // store the unique section name
2454
         uefi_header->unique_section.virtual_size = WILL_BE_FILLED_LATER; // same as pe_image_optional_header64.code_size
2455
         uefi_header->unique_section.virtual_address = ROUND_TO_UPPER_MULTIPLE (uefi_header->optional_header64.size_of_headers, uefi_header->optional_header64.file_alignment);
2456
         uefi_header->unique_section.rawdata_size = WILL_BE_FILLED_LATER; // same as pe_image_optional_header64.code_size
2457
         uefi_header->unique_section.rawdata_offset = uefi_header->unique_section.virtual_address;
2458
         uefi_header->unique_section.characteristics_bitmap = 0x60; // image contains code + image contains initialized data
2459
         ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, uefi_header->optional_header64.size_of_headers)); // pad as necessary
2460
      }
2461
      else // BIOS boot
2462
      {
41 pmbaty 2463
         // resolve the "bios.boot" file
2464
         resolved_pathname = resolve_pathname ("bios.boot", startup_entry_parms.search); // locate the executable location
2465
         ASSERT (resolved_pathname, "QNX BIOS boot \"bios.boot\" not found in search path");
2466
         ASSERT_WITH_ERRNO (Buffer_ReadFromFile (&boot_code, resolved_pathname)); // load it
2467
 
2468
         // isolate its first segment
2469
         #define ELFHDR ((elf_header_t *) boot_code.bytes) // this convenient definition will make sure the ELF header points at the right location
2470
         phdr = (elf_program_header_t *) &boot_code.bytes[ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_offset)]; // quick access to first program header
2471
         first_segment_offset = ELF_GET_NUMERIC (ELFHDR, phdr, file_offset); // get the first segment's start offset in the ELF file
2472
         cumulated_segment_length = ELF_GET_NUMERIC (ELFHDR, phdr, size_in_file); // get this ELF segment's length in the ELF file
2473
         #undef ELFHDR
2474
         ASSERT_WITH_ERRNO (Buffer_SubsetWithLength (&boot_code, first_segment_offset, cumulated_segment_length)); // isolate it
2475
 
2476
         // locate the "            boot" string (16 bytes) and chop it off along with everything preceding it
2477
         bootcode_start = Buffer_FindFirstByteArray (&boot_code, "            boot");
2478
         ASSERT (bootcode_start != NULL, "bios.boot does not contain the expected '            boot' marker");
2479
         bootcode_start += 16; // skip the marker
2480
         ASSERT_WITH_ERRNO (Buffer_SubsetFromTo (&boot_code, (size_t) bootcode_start - (size_t) boot_code.bytes, boot_code.size)); // isolate what's after
2481
 
2482
         // pad it to 3888 bytes (FIXME: why this value? Can it be less?)
2483
         ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&boot_code, 3888)); // pad as necessary
2484
 
2485
         ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&ifs.data, &boot_code)); // write boot blob
34 pmbaty 2486
         ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
2487
      }
1 pmbaty 2488
 
16 pmbaty 2489
      ifs.offsets.startupheader = ifs.data.size; // save startup header offset for future use
1 pmbaty 2490
      memset (&startup_header, 0, sizeof (startup_header)); // prepare startup header
2491
      memcpy (startup_header.signature, "\xeb\x7e\xff\x00", 4); // startup header signature, i.e. 0xff7eeb
2492
      startup_header.version       = 1;
26 pmbaty 2493
      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 2494
      startup_header.header_size   = sizeof (startup_header); // 256
2495
      if (strcmp (image_processor, "x86_64") == 0)
10 pmbaty 2496
         startup_header.machine = ELF_MACHINE_X86_64; // EM_X86_64
1 pmbaty 2497
      else if (strcmp (image_processor, "aarch64le") == 0)
10 pmbaty 2498
         startup_header.machine = ELF_MACHINE_AARCH64; // EM_AARCH64
1 pmbaty 2499
      else
15 pmbaty 2500
         DIE_WITH_EXITCODE (1, "unsupported processor type '%s' found in build file \"%s\"", image_processor, buildfile_pathname); // should not happen
41 pmbaty 2501
      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.*")
2502
      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)
2503
      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)
2504
      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)
2505
      startup_header.startup_size  = WILL_BE_FILLED_LATER;                   // [I ] Size of startup (never compressed), here 0x02f148 or 192 840 bytes
2506
      startup_header.stored_size   = WILL_BE_FILLED_LATER;                   // [I ] Size of entire image file (startup + *optionally compressed* imagefs) without optional boot prefix, here 0x00cd6128
2507
      startup_header.imagefs_size  = WILL_BE_FILLED_LATER;                   // [ S] Size of uncompressed imagefs, here 0x00ca6fe0 or 13 266 912 bytes
2508
      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 2509
      ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &startup_header, sizeof (startup_header))); // write startup header
2510
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2511
 
41 pmbaty 2512
      // prelink the startup executable, map its .text segment at the right virtual address for the IFS and stamp in its executable arguments
2513
      // then store the result in this entry's data blob
2514
      sprintf_s (textbase_mapaddr_string, sizeof (textbase_mapaddr_string), ".text=0x%zx", image_base + ifs.data.size); // image base + sizeof (boot prefix + startup header)
2515
      prelink (startup_entry_parms.search, // binary executable search path
2516
               textbase_mapaddr_string, // "--section-start" linker argument (e.g. ".text=0xXXXXXXXX")
2517
               &startup_argv, &startup_envp, // executable arguments (including its name) and environment to stamp in it
2518
               startup_entry_parms.should_keep_ld_output, // whether to keep the linker-produced output file
2519
               &startup_code, // buffer where to save the output
2520
               &startup_bootargs_offset); // where to save the offset of the boot arguments
2521
 
2522
      // 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)
2523
      ((bootargs_entry_t *) &startup_code.bytes[startup_bootargs_offset])->shdr_addr = (uint32_t) (image_base + boot_code.size);
2524
 
2525
      // backup two interesting values and chop off the ELF header until the start of the first segment
2526
      #define ELFHDR ((elf_header_t *) startup_code.bytes) // this convenient definition will make sure the ELF header points at the right location
2527
      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)
2528
      if (boot_type == BOOTTYPE_UEFI)
2529
      {
2530
         uefi_header = (uefi64_header_t *) ifs.data.bytes; // restore UEFI header pointer
2531
         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
2532
      }
2533
      first_segment_offset = 0;
2534
      minimal_padded_length = 0;
2535
      cumulated_segment_length = 0;
2536
      program_header_table_count = ELF_GET_NUMERIC (ELFHDR, ELFHDR, program_header_table_len); // get the number of program headers
2537
      for (table_index = 0; table_index < program_header_table_count; table_index++)
2538
      {
2539
         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
2540
         if (first_segment_offset == 0)
2541
            first_segment_offset = ELF_GET_NUMERIC (ELFHDR, phdr, file_offset); // get the first segment's start offset in the ELF file
2542
         minimal_padded_length += ELF_GET_NUMERIC (ELFHDR, phdr, size_in_memory); // get this ELF segment's required memory in the ELF file
2543
         cumulated_segment_length += ELF_GET_NUMERIC (ELFHDR, phdr, size_in_file); // get this ELF segment's length in the ELF file
2544
      }
2545
      #undef ELFHDR
2546
 
2547
      // FIXME: more bytes are read from the file past the last segment. Either a mkifs bug, or I'm wrong somewhere?
2548
      ASSERT_WITH_ERRNO (Buffer_SubsetWithLength (&startup_code, first_segment_offset, cumulated_segment_length + (boot_type == BOOTTYPE_UEFI ? 0x400 : 0x820)));
2549
 
2550
      // pad with zeroes up to the required memory length and align with page size plus 4 (FIXME: understand why +4)
2551
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&startup_code, ROUND_TO_UPPER_MULTIPLE (minimal_padded_length, image_pagesize) + 4)); // pad as necessary
2552
 
2553
      // write the startup blob and pad as necessary
2554
      ASSERT_WITH_ERRNO (Buffer_AppendBuffer (&ifs.data, &startup_code)); // write startup blob
16 pmbaty 2555
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2556
 
16 pmbaty 2557
      ifs.offsets.startuptrailer = ifs.data.size; // save startup trailer offset for future use
2558
      ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &startup_trailer, sizeof (startup_trailer))); // write startup trailer
2559
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2560
   }
2561
 
16 pmbaty 2562
   ifs.offsets.imageheader = ifs.data.size; // save image header offset for future use
1 pmbaty 2563
   memset (&image_header, 0, sizeof (image_header)); // prepare image header
2564
   memcpy (&image_header.signature, "imagefs", 7); // image filesystem signature, i.e. "imagefs"
2565
   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)
2566
   image_header.image_size    = WILL_BE_FILLED_LATER; // size from header to end of trailer (here 0xca6fe0 or 13 266 912)
7 pmbaty 2567
   image_header.hdr_dir_size  = WILL_BE_FILLED_LATER; // size from header to last dirent (here 0x12b8 or 4792)
1 pmbaty 2568
   image_header.dir_offset    = sizeof (image_header); // offset from header to first dirent (here 0x5c or 92)
2569
   image_header.boot_ino[0]   = image_kernel_ino; // inode of files for bootstrap p[ro?]g[ra?]ms (here 0xa0000002, 0, 0, 0)
2570
   image_header.script_ino    = image_bootscript_ino; // inode of file for script (here 3)
2571
   image_header.mountpoint[0] = '/'; // default mountpoint for image ("/" + "\0\0\0")
16 pmbaty 2572
   ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &image_header, sizeof (image_header))); // write image header
2573
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2574
 
6 pmbaty 2575
   // write image directory (with the wrong file offsets)
16 pmbaty 2576
   ifs.offsets.imagedir = ifs.data.size; // save image directory offset for future use
2577
   curr_offset = ifs.offsets.imagedir;
1 pmbaty 2578
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
16 pmbaty 2579
   {
2580
      Buffer_WriteIFSDirectoryEntryAt (&ifs.data, curr_offset, &fsentries[fsentry_index]); // write each dirent (the unknown fields will be fixed later)
2581
      curr_offset += fsentries[fsentry_index].header.size; // advance to the next one
2582
   }
2583
   ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&ifs.data, "\0\0\0\0")); // there seems to be 4 bytes of padding after the image directory
2584
   imgdir_size = ifs.data.size - ifs.offsets.imagedir; // measure image dir size and save it for future use
1 pmbaty 2585
 
20 pmbaty 2586
   // is it a bootable image with a startup file ?
41 pmbaty 2587
   if (startup_argv.count > 0)
1 pmbaty 2588
   {
30 pmbaty 2589
      // compute the kernel offset: address of the first page that comes after the directory entries
2590
      kernelfile_offset = ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_pagesize);
2591
 
26 pmbaty 2592
      // write the filesystem entries that may fit before the kernel
7 pmbaty 2593
      for (;;)
2594
      {
16 pmbaty 2595
         available_space = kernelfile_offset - ifs.data.size; // measure the available space until the kernel
7 pmbaty 2596
 
2597
         // look for the biggest one that can fit
2598
         largest_index = 0;
8 pmbaty 2599
         largest_size = 0;
7 pmbaty 2600
         for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2601
         {
2602
            if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written || (fsentries[fsentry_index].u.file.size > available_space))
2603
               continue; // skip all entries that don't have a separate data block, those who were written already and those that wouldn't fit
2604
            if (fsentries[fsentry_index].u.file.size > largest_size)
2605
            {
2606
               largest_size = fsentries[fsentry_index].u.file.size;
2607
               largest_index = fsentry_index;
2608
            }
2609
         }
8 pmbaty 2610
         if (largest_size == 0)
7 pmbaty 2611
            break; // found none ? if so, stop searching
16 pmbaty 2612
         fsentry_index = largest_index;
7 pmbaty 2613
 
16 pmbaty 2614
         fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2615
         Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
2616
         fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
7 pmbaty 2617
      }
16 pmbaty 2618
      LOG_INFO ("Last written offset: 0x%zx", ifs.data.size);
15 pmbaty 2619
      LOG_INFO ("Kernel file offset: 0x%zx", kernelfile_offset);
16 pmbaty 2620
      ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, kernelfile_offset)); // reach the kernel offset
1 pmbaty 2621
 
8 pmbaty 2622
      // now write the QNX kernel
2623
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2624
         if (fsentries[fsentry_index].header.ino == image_kernel_ino)
2625
            break; // locate the kernel directory entry (can't fail)
16 pmbaty 2626
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
35 pmbaty 2627
      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 2628
      ((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 2629
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write kernel file data
8 pmbaty 2630
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
1 pmbaty 2631
   }
2632
 
8 pmbaty 2633
   // then write all the other files by increasing inode number: ELF files first
7 pmbaty 2634
   for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
1 pmbaty 2635
   {
8 pmbaty 2636
      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 2637
          || (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 2638
         continue; // skip all entries that don't have a separate data block and those who were written already
16 pmbaty 2639
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2640
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
1 pmbaty 2641
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2642
   }
31 pmbaty 2643
   // other files (non-ELF, e.g. scripts and data files) last, in decreasing size order
2644
   for (;;)
8 pmbaty 2645
   {
31 pmbaty 2646
      // look for the biggest one that can fit
2647
      largest_index = 0;
2648
      largest_size = 0;
2649
      for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++)
2650
      {
2651
         if (!S_ISREG (fsentries[fsentry_index].header.mode) || fsentries[fsentry_index].UNSAVED_was_data_written)
2652
            continue; // skip all entries that don't have a separate data block, those who were written already and those that wouldn't fit
2653
         if (fsentries[fsentry_index].u.file.size > largest_size)
2654
         {
2655
            largest_size = fsentries[fsentry_index].u.file.size;
2656
            largest_index = fsentry_index;
2657
         }
2658
      }
2659
      if (largest_size == 0)
2660
         break; // found none ? if so, stop searching
2661
      fsentry_index = largest_index;
2662
 
16 pmbaty 2663
      fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure
2664
      Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write file data
8 pmbaty 2665
      fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written
2666
   }
16 pmbaty 2667
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2668
 
2669
   // finally, write trailer (including empty checksum)
16 pmbaty 2670
   ifs.offsets.imagetrailer = ifs.data.size; // save image trailer offset for future use
2671
   ASSERT_WITH_ERRNO (Buffer_Append (&ifs.data, &image_trailer, sizeof (image_trailer))); // write image trailer
2672
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, ROUND_TO_UPPER_MULTIPLE (ifs.data.size, image_align))); // pad as necessary
1 pmbaty 2673
 
2674
   // if we need to pad it to a specific length, do so
16 pmbaty 2675
   ASSERT_WITH_ERRNO (Buffer_PadWithZeroesTo (&ifs.data, image_totalsize));
2676
   ifs.final_size = ifs.data.size; // and this is the final size of the IFS
1 pmbaty 2677
 
2678
   // see if we are past the image max size, in which case it's an error
16 pmbaty 2679
   if (ifs.final_size > image_maxsize)
19 pmbaty 2680
      DIE_WITH_EXITCODE (1, "image file size %zd exceeds max size (%zd)", ifs.final_size, (size_t) image_maxsize);
1 pmbaty 2681
 
2 pmbaty 2682
   // do we have a startup file ? if so, this is a bootable image
41 pmbaty 2683
   if (startup_argv.count > 0)
1 pmbaty 2684
   {
16 pmbaty 2685
      // patch the startup header with its final values
2686
      startup_header.startup_size = (uint32_t) (ifs.offsets.imageheader - ifs.offsets.startupheader); // size of startup header up to image header
2687
      startup_header.imagefs_size = (uint32_t) (ifs.final_size - ifs.offsets.imageheader); // size of uncompressed imagefs
2688
      startup_header.ram_size     = (uint32_t) (ifs.final_size - ifs.offsets.startupheader);
2689
      startup_header.stored_size  = (uint32_t) (ifs.final_size - ifs.offsets.startupheader);
2690
      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 2691
   }
1 pmbaty 2692
 
8 pmbaty 2693
   // rewrite image header with final values
16 pmbaty 2694
   image_header.image_size = (uint32_t) (ifs.final_size - ifs.offsets.imageheader); // size of uncompressed imagefs
8 pmbaty 2695
   image_header.hdr_dir_size = sizeof (image_header) + (uint32_t) imgdir_size; // size from start of image header to last dirent
16 pmbaty 2696
   ASSERT_WITH_ERRNO (Buffer_WriteAt (&ifs.data, ifs.offsets.imageheader, &image_header, sizeof (image_header))); // write image header
8 pmbaty 2697
 
2698
   // rewrite image directory with final offset values
2699
   if (image_header.flags & IMAGE_FLAGS_SORTED)
16 pmbaty 2700
      qsort (&fsentries[1], fsentry_count - 1, sizeof (fsentry_t), fsentry_compare_pathnames_cb); // sort the filesystem entries by pathname if necessary
2701
   curr_offset = ifs.offsets.imagedir; // position ourselves at the beginning of the image directory
8 pmbaty 2702
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
16 pmbaty 2703
   {
2704
      Buffer_WriteIFSDirectoryEntryAt (&ifs.data, curr_offset, &fsentries[fsentry_index]); // rewrite each dirent
2705
      curr_offset += fsentries[fsentry_index].header.size; // advance to the next one
2706
   }
8 pmbaty 2707
 
2708
   // ALL CHECKSUMS AT THE VERY END
2709
 
26 pmbaty 2710
   // compute SHA-512 checksum and V1 checksum of image block
2711
   if (   ( (image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
2712
       || (!(image_header.flags & IMAGE_FLAGS_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__   )))
2713
      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
2714
   else
2715
      is_foreign_endianness = false; // else this header is for the same endianness as us
2716
 
2717
   if (image_header.flags & IMAGE_FLAGS_TRAILER_V2) // is it a V2 trailer ?
2718
   {
2719
      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
2720
      checksum = update_checksum (&ifs.data.bytes[ifs.offsets.imageheader], ifs.offsets.imagetrailer + SHA512_DIGEST_LENGTH - ifs.offsets.imageheader, is_foreign_endianness); // compute old checksum
2721
      memcpy (&ifs.data.bytes[ifs.offsets.imagetrailer + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
2722
   }
2723
   else // old V1 trailer
2724
   {
2725
      checksum = update_checksum (&ifs.data.bytes[ifs.offsets.imageheader], ifs.offsets.imagetrailer - ifs.offsets.imageheader, is_foreign_endianness); // compute old checksum
2726
      memcpy (&ifs.data.bytes[ifs.offsets.imagetrailer], &checksum, 4); // and write it in place
2727
   }
2728
 
2729
   // should we compress the image block ?
2730
   if (startup_header_compression_flag != STARTUP_HDR_FLAGS1_COMPRESS_NONE)
2731
   {
2732
      // it appears mkifs compresses data in blocks, prefixed by 2-byte block size in BIG ENDIAN
2733
      Buffer_InitWithSize (&compressed_imagefs, image_header.image_size * 11 / 10); // mallocate and add 10% for safety
2734
      compressed_imagefs.size = 0;
2735
      compressor_in = &ifs.data.bytes[ifs.offsets.imageheader]; // point at the start of the data to compress
2736
      compressor_out = &compressed_imagefs.bytes[2]; // point after the compressed block size word
2737
      remaining_len = ifs.data.size - ifs.offsets.imageheader; // see how many bytes there are to compress
2738
 
2739
      if (startup_header_compression_flag == STARTUP_HDR_FLAGS1_COMPRESS_UCL)
2740
         ASSERT (ucl_init () == UCL_E_OK, "UCL library initialization failed -- please recompile this tool with less aggressive optimizations");
2741
      else if (startup_header_compression_flag == STARTUP_HDR_FLAGS1_COMPRESS_LZO)
2742
         ASSERT (ucl_init () == UCL_E_OK, "LZO library initialization failed -- please recompile this tool with less aggressive optimizations");
2743
      else if (startup_header_compression_flag == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB)
41 pmbaty 2744
         DIE_WITH_EXITCODE (1, "unimplemented compression scheme: zlib"); // deprecated by QNX: won't fix
26 pmbaty 2745
      else
2746
         DIE_WITH_EXITCODE (1, "unsupported compression flags: 0x%2x", startup_header_compression_flag);
2747
 
2748
      // run the compressible payload (the imagefs) through the right compression algorithm
2749
      while (remaining_len > 0)
2750
      {
2751
         compressor_inlen = (ucl_uint) remaining_len; // size the compressor input appropriately
2752
         if (compressor_inlen > 65536)
2753
            compressor_inlen = 65536; // cap it to a VERY conservative value
2754
         if (startup_header_compression_flag == STARTUP_HDR_FLAGS1_COMPRESS_UCL)
2755
         {
2756
            // 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
2757
            static ucl_uint ucl_outlen; // have a different variable because of pointer size mismatch
33 pmbaty 2758
            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))
2759
               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 2760
            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
2761
            compressor_outlen = ucl_outlen; // cast back to size_t
2762
         }
2763
         else if (startup_header_compression_flag == STARTUP_HDR_FLAGS1_COMPRESS_LZO)
2764
         {
2765
            // 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.
2766
            static lzo_align_t lzo_workmem[(LZO1X_1_MEM_COMPRESS + (sizeof (lzo_align_t) - 1)) / sizeof(lzo_align_t)]; // heap-allocated aligned buffer
2767
            static lzo_uint lzo_outlen; // have a different variable because of pointer size mismatch
2768
            while (((compressor_ret = lzo1x_1_compress (compressor_in, compressor_inlen, compressor_out, &lzo_outlen, lzo_workmem)) == UCL_E_OK) && (lzo_outlen > 0xFFFF))
33 pmbaty 2769
               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 2770
            ASSERT (compressor_ret == LZO_E_OK, "LZO compression error: lzo1x_1_compress() returned %d", compressor_ret); // make sure it's not a compression error
2771
            compressor_outlen = lzo_outlen; // cast back to size_t
2772
         }
2773
         else if (startup_header_compression_flag == STARTUP_HDR_FLAGS1_COMPRESS_ZLIB)
2774
         {
2775
            // Zlib (TODO)
2776
         }
2777
 
2778
         // the compression produced a block smaller than 65536 bytes
30 pmbaty 2779
         //LOG_DEBUG ("compressed block size %zd", compressor_outlen);
26 pmbaty 2780
         compressed_imagefs.bytes[compressed_imagefs.size + 0] = (uint8_t) (compressor_outlen >> 8); // write compressed block size word (in big endian)
2781
         compressed_imagefs.bytes[compressed_imagefs.size + 1] = (uint8_t) (compressor_outlen >> 0);
2782
         compressed_imagefs.size += 2 + (size_t) compressor_outlen; // advance in compression buffer by the compressed block size word plus the compressed block size
2783
 
2784
         remaining_len -= compressor_inlen; // see how many bytes remain to compress
2785
 
2786
         compressor_in += compressor_inlen; // advance in input stream
2787
         compressor_out += 2 + compressor_outlen; // advance in output stream
2788
      }
2789
 
2790
      compressed_imagefs.bytes[compressed_imagefs.size + 0] = 0; // write the end of stream marker (empty block with nil size)
2791
      compressed_imagefs.bytes[compressed_imagefs.size + 1] = 0;
2792
      compressed_imagefs.size += 2;
2793
      LOG_INFO ("compressed %zd bytes into %zd bytes\n", ifs.data.size - ifs.offsets.imageheader, compressed_imagefs.size);
2794
 
2795
      /////////////////////////////////////////////////////////////
2796
      // WARNING: ALL IFS OFFSETS BECOME INVALID PAST THIS POINT //
2797
      /////////////////////////////////////////////////////////////
2798
 
2799
      // now place the compressed buffer in the payload at the imagefs offset
2800
      ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (&ifs.data, ifs.offsets.imageheader, &compressed_imagefs));
2801
      ifs.data.size = ifs.offsets.imageheader + compressed_imagefs.size; // update IFS data size
2802
      Buffer_Forget (&compressed_imagefs);
2803
 
2804
      // fix the stored size in the startup header
2805
      startup_header.stored_size = (uint32_t) (ifs.data.size - ifs.offsets.startupheader);
2806
      ASSERT_WITH_ERRNO (Buffer_WriteAt (&ifs.data, ifs.offsets.startupheader, &startup_header, sizeof (startup_header))); // write the final startup header at its right offset
2807
   }
2808
 
8 pmbaty 2809
   // do we have a startup file ? if so, this is a bootable image
41 pmbaty 2810
   if (startup_argv.count > 0)
8 pmbaty 2811
   {
34 pmbaty 2812
      if (boot_type == BOOTTYPE_UEFI) // UEFI boot: fix the final offsets and sizes in the EFI executable's PE header
2813
      {
2814
         uefi_header = (uefi64_header_t *) ifs.data.bytes; // have a convenience pointer
2815
         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
2816
         uefi_header->optional_header64.image_size = (uint32_t) ifs.data.size; // total IFS file size
2817
         uefi_header->unique_section.virtual_size = uefi_header->optional_header64.code_size; // same as pe_image_optional_header64.code_size
2818
         uefi_header->unique_section.rawdata_size = uefi_header->optional_header64.code_size; // same as pe_image_optional_header64.code_size
2819
      }
2820
 
1 pmbaty 2821
      // compute SHA-512 checksum and V1 checksum of startup block
6 pmbaty 2822
      if (   ( (startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
26 pmbaty 2823
          || (!(startup_header.flags1 & STARTUP_HDR_FLAGS1_BIGENDIAN) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__   )))
6 pmbaty 2824
         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
2825
      else
2826
         is_foreign_endianness = false; // else this header is for the same endianness as us
1 pmbaty 2827
 
16 pmbaty 2828
      if (startup_header.flags1 & STARTUP_HDR_FLAGS1_TRAILER_V2) // is it a V2 trailer ?
2829
      {
2830
         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
2831
         checksum = update_checksum (&ifs.data.bytes[ifs.offsets.startupheader], ifs.offsets.startuptrailer + SHA512_DIGEST_LENGTH - ifs.offsets.startupheader, is_foreign_endianness); // compute old checksum
2832
         memcpy (&ifs.data.bytes[ifs.offsets.startuptrailer + SHA512_DIGEST_LENGTH], &checksum, 4); // and write it in place
2833
      }
2834
      else // old V1 trailer
2835
      {
2836
         checksum = update_checksum (&ifs.data.bytes[ifs.offsets.startupheader], ifs.offsets.startuptrailer - ifs.offsets.startupheader, is_foreign_endianness); // compute old checksum
2837
         memcpy (&ifs.data.bytes[ifs.offsets.startuptrailer], &checksum, 4); // and write it in place
2838
      }
1 pmbaty 2839
   }
2840
 
16 pmbaty 2841
   // now rewrite IFS with the correct checksums
19 pmbaty 2842
   ASSERT_WITH_ERRNO (Buffer_WriteToFile (&ifs.data, (ifs_pathname != NULL ? ifs_pathname : "<stdout>")));
14 pmbaty 2843
 
16 pmbaty 2844
   // finished, cleanup
2845
   for (fsentry_index = 0; fsentry_index < fsentry_count; fsentry_index++)
14 pmbaty 2846
   {
19 pmbaty 2847
      fsentry = &fsentries[fsentry_index]; // quick access to filesystem entry
2848
      if (S_ISDIR (fsentry->header.mode))
2849
         free (fsentry->u.dir.path);
2850
      else if (S_ISLNK (fsentry->header.mode))
2851
      {
2852
         free (fsentry->u.symlink.path);
2853
         free (fsentry->u.symlink.contents);
2854
      }
2855
      else if (S_ISREG (fsentry->header.mode))
2856
      {
2857
         free (fsentry->u.file.path);
34 pmbaty 2858
         free (fsentry->UNSAVED_databuf);
19 pmbaty 2859
      }
2860
      else if (S_ISFIFO (fsentry->header.mode))
2861
         free (fsentry->u.device.path);
14 pmbaty 2862
   }
2863
 
16 pmbaty 2864
   // and exit with a success code
2865
   LOG_INFO ("Success");
2866
   exit (0);
14 pmbaty 2867
}