Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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