Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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