Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 18 | Rev 20 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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