Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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